Skip to content

Starting with Dio

For REST Api request, here we will use DIO Package.

Dio Setup with Riverpod This setup ensures a single Dio instance (Singleton), centralizes configuration (BaseOptions), and maps low-level DioException to your custom AppException classes automatically.

Core Dio Client (handles exceptions)

dart

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// Reuse AppExceptions
class NetworkException extends AppException { /* ... */ }
class UnauthorizedException extends AppException { /* ... */ }
class ServerException extends AppException { /* ... */ }
class ApiClient {
late final Dio _dio;
ApiClient() {
_dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com/',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
headers: {'Content-Type': 'application/json'},
));
_dio.interceptors.addAll([
LogInterceptor(responseBody: true, error: true),
InterceptorsWrapper(
onRequest: (options, handler) {
// token: options.headers['Authorization'] = ...;
handler.next(options);
},
onError: (e, handler) => handler.next(e),
),
]);
}
Future<T> get<T>(String path, {Map<String, dynamic>? query}) async {
try {
final res = await _dio.get(path, queryParameters: query);
return res.data as T;
} on DioException catch (e, st) {
_mapDioError(e, st);
} catch (e, st) {
throw NetworkException('Unexpected', cause: e, stackTrace: st);
}
throw const NetworkException('Unknown'); // unreachable
}
Future<T> post<T>(String path, dynamic data) async {
try {
final res = await _dio.post(path, data: data);
return res.data as T;
} on DioException catch (e, st) {
_mapDioError(e, st);
} catch (e, st) {
throw NetworkException('Unexpected', cause: e, stackTrace: st);
}
}
void _mapDioError(DioException e, StackTrace st) {
final status = e.response?.statusCode;
if (status == 401) throw UnauthorizedException('Unauthorized', cause: e, stackTrace: st);
else if ([408, 500, 502, 503, 504].contains(status))
throw ServerException('Server error', cause: e, stackTrace: st);
else if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.receiveTimeout ||
e.type == DioExceptionType.connectionError)
throw NetworkException('Network', cause: e, stackTrace: st);
throw ServerException(e.message ?? 'Failed', cause: e, stackTrace: st);
}
}

2) Providers (providers.dart)

dart

// Singleton-like ApiClient (keepAlive: true for app-life)
final apiProvider = Provider<ApiClient>((_) => ApiClient());
// Repo (depends on ApiClient)
final userRepoProvider = Provider<UserRepo>((ref) =>
UserRepo(ref.read(apiProvider))
);

Now Do Dependency injection with your service or repo classes.