174 lines
5.3 KiB
Dart
174 lines
5.3 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
class NetworkApiService {
|
|
static final NetworkApiService _instance = NetworkApiService._internal();
|
|
late Dio _dio;
|
|
|
|
factory NetworkApiService() {
|
|
return _instance;
|
|
}
|
|
|
|
NetworkApiService._internal() {
|
|
_dio = Dio(
|
|
BaseOptions(
|
|
connectTimeout: const Duration(seconds: 30),
|
|
receiveTimeout: const Duration(seconds: 30),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
),
|
|
);
|
|
// === RETRY INTERCEPTOR (for timeouts & connection errors) ===
|
|
_dio.interceptors.add(InterceptorsWrapper(
|
|
onError: (DioException err, handler) async {
|
|
final options = err.requestOptions;
|
|
const maxRetries = 2; // Total attempts = 1 initial + 2 retry
|
|
final currentRetry = options.extra['retry'] as int? ?? 0;
|
|
|
|
final shouldRetry = currentRetry < maxRetries &&
|
|
(err.type == DioExceptionType.connectionTimeout ||
|
|
err.type == DioExceptionType.sendTimeout ||
|
|
err.type == DioExceptionType.receiveTimeout );
|
|
if (shouldRetry) {
|
|
if (kDebugMode) {
|
|
print('🔁 Retrying request (${currentRetry + 1}) to ${options.uri}');
|
|
}
|
|
// Wait before retrying
|
|
// await Future.delayed(const Duration(seconds: 1));
|
|
options.extra['retry'] = currentRetry + 1;
|
|
// Re-execute the request
|
|
try {
|
|
final response = await _dio.fetch(options);
|
|
return handler.resolve(response);
|
|
} on DioException catch (e) {
|
|
return handler.reject(e);
|
|
}
|
|
}
|
|
// Not retrying → propagate original error
|
|
return handler.reject(err);
|
|
},
|
|
));
|
|
// === MAIN INTERCEPTOR (logging, auth, etc.) ===
|
|
_dio.interceptors.add(
|
|
InterceptorsWrapper(
|
|
onRequest: (options, handler) {
|
|
// Add token if available (uncomment when needed)
|
|
// String? token = "your_token_here";
|
|
// if (token != null) {
|
|
// options.headers['Authorization'] = 'Bearer $token';
|
|
// }
|
|
|
|
if (kDebugMode) {
|
|
print('📤 REQUEST[${options.method}] => URL: ${options.uri}');
|
|
}
|
|
return handler.next(options);
|
|
},
|
|
onResponse: (response, handler) {
|
|
if (kDebugMode) {
|
|
print('📥 RESPONSE[${response.statusCode}] => DATA: ${response.data}');
|
|
}
|
|
return handler.next(response);
|
|
},
|
|
onError: (error, handler) {
|
|
if (kDebugMode) {
|
|
print('❌ ERROR[${error.response?.statusCode}] => MESSAGE: ${error.message}');
|
|
}
|
|
return handler.next(error);
|
|
},
|
|
),
|
|
);
|
|
// === DIO LOGGING INTERCEPTOR (debug only) ===
|
|
if (kDebugMode) {
|
|
_dio.interceptors.add(
|
|
LogInterceptor(
|
|
request: true,
|
|
requestHeader: true,
|
|
requestBody: true,
|
|
responseHeader: false,
|
|
responseBody: true,
|
|
error: true,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
// GET API Request
|
|
Future<Response> getApi({
|
|
required String url,
|
|
Map<String, dynamic>? queryParameters,
|
|
Options? options,
|
|
CancelToken? cancelToken,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.get(
|
|
url,
|
|
queryParameters: queryParameters,
|
|
options: options,
|
|
cancelToken: cancelToken,
|
|
);
|
|
return response;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
// POST API Request
|
|
Future<Response> postApi({
|
|
required String url,
|
|
dynamic data,
|
|
Map<String, dynamic>? queryParameters,
|
|
Options? options,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onSendProgress,
|
|
}) async {
|
|
try {
|
|
final response = await _dio.post(
|
|
url,
|
|
data: data,
|
|
queryParameters: queryParameters,
|
|
options: options,
|
|
cancelToken: cancelToken,
|
|
onSendProgress: onSendProgress,
|
|
);
|
|
return response;
|
|
} on DioException catch (e) {
|
|
throw _handleError(e);
|
|
}
|
|
}
|
|
// Error Handler
|
|
String _handleError(DioException error) {
|
|
String errorDescription = "";
|
|
switch (error.type) {
|
|
case DioExceptionType.connectionTimeout:
|
|
errorDescription = "Connection timeout. Please try again.";
|
|
break;
|
|
case DioExceptionType.sendTimeout:
|
|
errorDescription = "Send timeout. Please try again.";
|
|
break;
|
|
case DioExceptionType.receiveTimeout:
|
|
errorDescription = "Receive timeout. Please try again.";
|
|
break;
|
|
case DioExceptionType.badCertificate:
|
|
errorDescription = "Bad certificate.";
|
|
break;
|
|
case DioExceptionType.badResponse:
|
|
errorDescription = error.response?.data['message'] ??
|
|
"Received invalid status code: ${error.response?.statusCode}";
|
|
break;
|
|
case DioExceptionType.cancel:
|
|
errorDescription = "Request was cancelled.";
|
|
break;
|
|
case DioExceptionType.connectionError:
|
|
errorDescription = "No internet connection.";
|
|
break;
|
|
case DioExceptionType.unknown:
|
|
errorDescription = "Something went wrong. Please try again.";
|
|
break;
|
|
}
|
|
return errorDescription;
|
|
}
|
|
// Update headers (e.g., add auth token)
|
|
void updateHeaders(Map<String, dynamic> headers) {
|
|
_dio.options.headers.addAll(headers);
|
|
}
|
|
} |