import 'package:dio/dio.dart'; import '../constants/app_constants.dart'; import '../errors/exceptions.dart'; /// API client for making HTTP requests using Dio class ApiClient { late final Dio _dio; ApiClient() { _dio = Dio( BaseOptions( baseUrl: AppConstants.apiBaseUrl, connectTimeout: const Duration(milliseconds: AppConstants.connectionTimeout), receiveTimeout: const Duration(milliseconds: AppConstants.receiveTimeout), sendTimeout: const Duration(milliseconds: AppConstants.sendTimeout), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, ), ); // Add request/response interceptors for logging and error handling _dio.interceptors.add( InterceptorsWrapper( onRequest: (options, handler) { // Log request details in debug mode handler.next(options); }, onResponse: (response, handler) { // Log response details in debug mode handler.next(response); }, onError: (error, handler) { // Handle different types of errors _handleDioError(error); handler.next(error); }, ), ); } /// Make a GET request Future> get( String path, { Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { try { return await _dio.get( path, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); } on DioException catch (e) { throw _handleDioError(e); } } /// Make a POST request Future> post( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { try { return await _dio.post( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); } on DioException catch (e) { throw _handleDioError(e); } } /// Make a PUT request Future> put( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { try { return await _dio.put( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); } on DioException catch (e) { throw _handleDioError(e); } } /// Make a DELETE request Future> delete( String path, { dynamic data, Map? queryParameters, Options? options, CancelToken? cancelToken, }) async { try { return await _dio.delete( path, data: data, queryParameters: queryParameters, options: options, cancelToken: cancelToken, ); } on DioException catch (e) { throw _handleDioError(e); } } /// Handle Dio errors and convert them to custom exceptions Exception _handleDioError(DioException error) { switch (error.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return const NetworkException('Connection timeout. Please check your internet connection.'); case DioExceptionType.badResponse: final statusCode = error.response?.statusCode; final message = error.response?.data?['message'] ?? 'Server error occurred'; if (statusCode != null) { if (statusCode >= 400 && statusCode < 500) { return ServerException('Client error: $message (Status: $statusCode)'); } else if (statusCode >= 500) { return ServerException('Server error: $message (Status: $statusCode)'); } } return ServerException('HTTP error: $message'); case DioExceptionType.cancel: return const NetworkException('Request was cancelled'); case DioExceptionType.connectionError: return const NetworkException('No internet connection. Please check your network settings.'); case DioExceptionType.badCertificate: return const NetworkException('Certificate verification failed'); case DioExceptionType.unknown: default: return ServerException('An unexpected error occurred: ${error.message}'); } } /// Add authorization header void addAuthorizationHeader(String token) { _dio.options.headers['Authorization'] = 'Bearer $token'; } /// Remove authorization header void removeAuthorizationHeader() { _dio.options.headers.remove('Authorization'); } /// Update base URL (useful for different environments) void updateBaseUrl(String newBaseUrl) { _dio.options.baseUrl = newBaseUrl; } }