HTTP & APIsintermediate

Dio API Service

Production-ready Dio HTTP client with interceptors, token refresh, and error handling.

#dio#http#api#interceptors
dart
1import 'package:dio/dio.dart';
2 
3class ApiService {
4 static const _baseUrl = 'https://api.example.com/v1';
5 
6 late final Dio _dio;
7 
8 ApiService() {
9 _dio = Dio(BaseOptions(
10 baseUrl: _baseUrl,
11 connectTimeout: const Duration(seconds: 10),
12 receiveTimeout: const Duration(seconds: 15),
13 headers: {'Content-Type': 'application/json'},
14 ))
15 ..interceptors.addAll([
16 _AuthInterceptor(),
17 _LoggingInterceptor(),
18 _RetryInterceptor(dio: _dio),
19 ]);
20 }
21 
22 Future<T> get<T>(String path, {Map<String, dynamic>? params, required T Function(dynamic) fromJson}) async {
23 try {
24 final response = await _dio.get(path, queryParameters: params);
25 return fromJson(response.data);
26 } on DioException catch (e) {
27 throw _handleError(e);
28 }
29 }
30 
31 Future<T> post<T>(String path, {required Map<String, dynamic> data, required T Function(dynamic) fromJson}) async {
32 try {
33 final response = await _dio.post(path, data: data);
34 return fromJson(response.data);
35 } on DioException catch (e) {
36 throw _handleError(e);
37 }
38 }
39 
40 Exception _handleError(DioException e) {
41 return switch (e.type) {
42 DioExceptionType.connectionTimeout => const TimeoutException(),
43 DioExceptionType.receiveTimeout => const TimeoutException(),
44 DioExceptionType.badResponse => switch (e.response?.statusCode) {
45 401 => const UnauthorizedException(),
46 403 => const ForbiddenException(),
47 404 => const NotFoundException(),
48 _ => ServerException(e.response?.statusCode ?? 500),
49 },
50 _ => const NetworkException(),
51 };
52 }
53}
54 
55class _AuthInterceptor extends Interceptor {
56 @override
57 void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
58 final token = TokenStorage.read();
59 if (token != null) {
60 options.headers['Authorization'] = 'Bearer $token';
61 }
62 handler.next(options);
63 }
64 
65 @override
66 void onError(DioException err, ErrorInterceptorHandler handler) async {
67 if (err.response?.statusCode == 401) {
68 try {
69 await AuthService.refreshToken();
70 final retryOptions = err.requestOptions;
71 retryOptions.headers['Authorization'] = 'Bearer ${TokenStorage.read()}';
72 final response = await Dio().fetch(retryOptions);
73 handler.resolve(response);
74 return;
75 } catch (_) {
76 AuthService.logout();
77 }
78 }
79 handler.next(err);
80 }
81}