Есть ли у вас опыт работы с Retrofit в Dart/Flutter?

«Есть ли у вас опыт работы с Retrofit в Dart/Flutter?» — вопрос из категории Сети, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, я использовал пакет retrofit в сочетании с dio и retrofit_generator для декларативного описания API-клиентов в Flutter-приложениях. Этот подход позволяет сгенерировать весь шаблонный HTTP-код, сосредоточившись только на определении endpoints и их параметров.

Как я настраиваю проект: В pubspec.yaml добавляю зависимости:

dependencies:
  dio: ^5.0.0
  retrofit: ^4.0.0

dev_dependencies:
  retrofit_generator: ^4.0.0
  build_runner: ^2.0.0

Пример декларативного API-сервиса для работы с постами:

import 'package:dio/dio.dart';
import 'package:retrofit/retrofit.dart';
import 'package:json_annotation/json_annotation.dart';

import 'models/post.dart'; // Модель, созданная с json_serializable

part 'api_service.g.dart'; // Файл для кодогенерации

@RestApi(baseUrl: "https://jsonplaceholder.typicode.com")
abstract class ApiService {
  factory ApiService(Dio dio, {String baseUrl}) = _ApiService;

  // GET запрос с path-параметром
  @GET("/posts/{id}")
  Future<Post> getPost(@Path("id") int id);

  // GET запрос с query-параметрами и кастомным header
  @GET("/posts")
  @Headers({"Custom-Header": "my-value"})
  Future<List<Post>> getPostsForUser(
    @Query("userId") int userId,
    @Query("_sort") String sortBy, // Например, "id"
  );

  // POST запрос с телом в формате JSON
  @POST("/posts")
  Future<Post> createPost(@Body() Post post);

  // Multipart запрос для загрузки файла
  @POST("/upload")
  @MultiPart()
  Future<void> uploadFile(@Part() File file, @Part() String description);
}

Инициализация и использование в коде:

void main() {
  // 1. Создаём Dio-клиент с настройками
  final dio = Dio();
  dio.options.connectTimeout = Duration(seconds: 30);
  dio.interceptors.add(LogInterceptor()); // Логирование запросов

  // 2. Создаём экземпляр сгенерированного API-клиента
  final apiService = ApiService(dio);

  // 3. Используем как обычный асинхронный метод
  runApp(MyApp(apiService: apiService));
}

// В бизнес-логике или ViewModel
Future<void> fetchData() async {
  try {
    final post = await apiService.getPost(1);
    final userPosts = await apiService.getPostsForUser(5, "id");
    // Обработка данных...
  } on DioException catch (e) {
    // Обработка ошибок сети/сервера
    print('Error: ${e.response?.statusCode}');
  }
}

Запуск генерации: После определения абстрактного класса запускаю:

flutter pub run build_runner build

Генератор создаст класс _ApiService в файле api_service.g.dart, который содержит всю реализацию HTTP-вызовов с использованием Dio.

Преимущества, которые я получил:

  • Чистота и читаемость: Интерфейс API становится само-документируемым.
  • Безопасность типов: Параметры и возвращаемые значения строго типизированы.
  • Снижение ошибок: Уходит ручное формирование URL, query-параметров и headers.
  • Мощь Dio: Сохраняются все возможности Dio (интерцепторы, трансформеры, отмена запросов).

Ограничения: Для очень нестандартных запросов иногда проще использовать чистый Dio, но в 95% случаев Retrofit полностью покрывает потребности.