import 'package:dio/dio.dart';
import 'package:dio_retry/dio_retry.dart';
class ApiService {
late Dio _dio;
static final ApiService _instance = ApiService._internal();
factory ApiService() => _instance;
ApiService._internal() {
_dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 5),
receiveTimeout: Duration(seconds: 3),
sendTimeout: Duration(seconds: 3),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
));
_setupInterceptors();
}
void _setupInterceptors() {
// 1. Log Interceptor
_dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
error: true,
));
// 2. Auth Interceptor
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
final token = _getToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
return handler.next(options);
},
));
// 3. Retry Interceptor
_dio.interceptors.add(
RetryInterceptor(
dio: _dio,
options: RetryOptions(
retries: 3,
retryInterval: Duration(seconds: 2),
exponentialBackoff: true,
),
),
);
// 4. Error Interceptor
_dio.interceptors.add(InterceptorsWrapper(
onError: (error, handler) {
_handleError(error);
return handler.next(error);
},
));
}
String? _getToken() {
// Lấy token từ secure storage
// return SecureStorage.getToken();
return null;
}
void _handleError(DioException error) {
// Xử lý lỗi tập trung
// Có thể hiển thị snackbar, log, etc.
print('API Error: ${error.message}');
}
// GET request
Future<Response<T>> get<T>(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
return await _dio.get<T>(
path,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
} on DioException catch (e) {
throw _mapException(e);
}
}
// POST request
Future<Response<T>> post<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
return await _dio.post<T>(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
} on DioException catch (e) {
throw _mapException(e);
}
}
// PUT request
Future<Response<T>> put<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
return await _dio.put<T>(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
} on DioException catch (e) {
throw _mapException(e);
}
}
// DELETE request
Future<Response<T>> delete<T>(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
return await _dio.delete<T>(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
} on DioException catch (e) {
throw _mapException(e);
}
}
Exception _mapException(DioException error) {
switch (error.type) {
case DioExceptionType.connectionTimeout:
case DioExceptionType.sendTimeout:
case DioExceptionType.receiveTimeout:
return TimeoutException('Request timeout');
case DioExceptionType.badResponse:
return ServerException(
'Server error: ${error.response?.statusCode}',
error.response?.statusCode,
);
case DioExceptionType.cancel:
return CancelException('Request cancelled');
default:
return NetworkException('Network error: ${error.message}');
}
}
}
// Custom exceptions
class TimeoutException implements Exception {
final String message;
TimeoutException(this.message);
}
class ServerException implements Exception {
final String message;
final int? statusCode;
ServerException(this.message, this.statusCode);
}
class CancelException implements Exception {
final String message;
CancelException(this.message);
}
class NetworkException implements Exception {
final String message;
NetworkException(this.message);
}