Настройка Dio для сетевых запросов во Flutter-приложении
http пакет из Dart-экосистемы покрывает базовые потребности, но в продакшн-приложении быстро появляются задачи, которые он решает неудобно: интерцепторы для авторизации, автоматический retry, логирование, отмена запросов, multipart-загрузка файлов с прогрессом. Dio закрывает всё это из коробки.
Установка и базовая конфигурация
dependencies:
dio: ^5.4.0
Создаём singleton через get_it или riverpod:
final dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com/v1',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
headers: {'Accept': 'application/json'},
));
Интерцепторы — ключевая часть настройки
Auth интерцептор добавляет токен к каждому запросу и обрабатывает 401:
class AuthInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
final token = tokenStorage.accessToken;
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
try {
await tokenStorage.refresh();
// Повторяем оригинальный запрос с новым токеном
final opts = err.requestOptions;
opts.headers['Authorization'] = 'Bearer ${tokenStorage.accessToken}';
final response = await dio.fetch(opts);
handler.resolve(response);
return;
} catch (_) {
// refresh failed — logout
}
}
handler.next(err);
}
}
Логирование в dev-режиме:
if (kDebugMode) {
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
logPrint: (o) => debugPrint(o.toString()),
));
}
Retry интерцептор для сетевых ошибок — используем dio_smart_retry:
dio.interceptors.add(RetryInterceptor(
dio: dio,
retries: 3,
retryDelays: [
Duration(seconds: 1),
Duration(seconds: 2),
Duration(seconds: 3),
],
));
Загрузка файлов и отмена запросов
// Multipart upload с прогрессом
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(filePath, filename: 'photo.jpg'),
});
await dio.post('/upload', data: formData,
onSendProgress: (sent, total) {
progress.value = sent / total;
},
);
// Отмена запроса
final cancelToken = CancelToken();
dio.get('/data', cancelToken: cancelToken);
// Позже:
cancelToken.cancel('User navigated away');
CancelToken — обязателен для запросов, привязанных к жизненному циклу виджета. Не отменять запросы в dispose() — утечка памяти и возможные setState после dispose.
Обработка ошибок
DioException содержит type — важно различать:
-
DioExceptionType.connectionTimeout— нет интернета или сервер недоступен -
DioExceptionType.badResponse— сервер вернул 4xx/5xx -
DioExceptionType.cancel— запрос отменён
Оборачиваем в domain-слой, чтобы не тащить Dio-зависимость в BLoC/Cubit:
Future<Either<Failure, T>> safeCall<T>(Future<T> Function() request) async {
try {
return Right(await request());
} on DioException catch (e) {
return Left(NetworkFailure.fromDioException(e));
}
}
Сроки
Базовая настройка Dio с auth-интерцептором и логированием: 4–8 часов. С retry, обработкой ошибок и интеграцией в архитектуру проекта (BLoC, Riverpod): 1–2 дня. Стоимость рассчитывается индивидуально.







