feat: Implement ApiService with Dio for standardized API requests and responses using ApiResponse entity.

This commit is contained in:
Achintha Isuru
2026-02-25 10:05:41 -05:00
parent 12211e54e2
commit 71c1610c0e
5 changed files with 162 additions and 0 deletions

View File

@@ -8,3 +8,4 @@ export 'src/presentation/mixins/bloc_error_handler.dart';
export 'src/presentation/observers/core_bloc_observer.dart'; export 'src/presentation/observers/core_bloc_observer.dart';
export 'src/config/app_config.dart'; export 'src/config/app_config.dart';
export 'src/routing/routing.dart'; export 'src/routing/routing.dart';
export 'src/services/api_service.dart';

View File

@@ -0,0 +1,135 @@
import 'package:dio/dio.dart';
import 'package:krow_domain/krow_domain.dart';
/// A service that handles HTTP communication using the [Dio] client.
///
/// This class provides a wrapper around [Dio]'s methods to handle
/// response parsing and error handling in a consistent way.
class ApiService {
/// Creates an [ApiService] with the given [Dio] instance.
ApiService(this._dio);
/// The underlying [Dio] client used for network requests.
final Dio _dio;
/// Performs a GET request to the specified [endpoint].
Future<ApiResponse> get(
String endpoint, {
Map<String, dynamic>? params,
}) async {
try {
final Response<dynamic> response = await _dio.get<dynamic>(
endpoint,
queryParameters: params,
);
return _handleResponse(response);
} on DioException catch (e) {
return _handleError(e);
}
}
/// Performs a POST request to the specified [endpoint].
Future<ApiResponse> post(
String endpoint, {
dynamic data,
Map<String, dynamic>? params,
}) async {
try {
final Response<dynamic> response = await _dio.post<dynamic>(
endpoint,
data: data,
queryParameters: params,
);
return _handleResponse(response);
} on DioException catch (e) {
return _handleError(e);
}
}
/// Performs a PUT request to the specified [endpoint].
Future<ApiResponse> put(
String endpoint, {
dynamic data,
Map<String, dynamic>? params,
}) async {
try {
final Response<dynamic> response = await _dio.put<dynamic>(
endpoint,
data: data,
queryParameters: params,
);
return _handleResponse(response);
} on DioException catch (e) {
return _handleError(e);
}
}
/// Performs a PATCH request to the specified [endpoint].
Future<ApiResponse> patch(
String endpoint, {
dynamic data,
Map<String, dynamic>? params,
}) async {
try {
final Response<dynamic> response = await _dio.patch<dynamic>(
endpoint,
data: data,
queryParameters: params,
);
return _handleResponse(response);
} on DioException catch (e) {
return _handleError(e);
}
}
/// Extracts [ApiResponse] from a successful [Response].
ApiResponse _handleResponse(Response<dynamic> response) {
if (response.data is Map<String, dynamic>) {
final Map<String, dynamic> body = response.data as Map<String, dynamic>;
return ApiResponse(
code:
body['code']?.toString() ??
response.statusCode?.toString() ??
'unknown',
message: body['message']?.toString() ?? 'Success',
data: body['data'],
errors: _parseErrors(body['errors']),
);
}
return ApiResponse(
code: response.statusCode?.toString() ?? '200',
message: 'Success',
data: response.data,
);
}
/// Extracts [ApiResponse] from a [DioException].
ApiResponse _handleError(DioException e) {
if (e.response?.data is Map<String, dynamic>) {
final Map<String, dynamic> body =
e.response!.data as Map<String, dynamic>;
return ApiResponse(
code:
body['code']?.toString() ??
e.response?.statusCode?.toString() ??
'error',
message: body['message']?.toString() ?? e.message ?? 'Error occurred',
data: body['data'],
errors: _parseErrors(body['errors']),
);
}
return ApiResponse(
code: e.response?.statusCode?.toString() ?? 'error',
message: e.message ?? 'Unknown error',
errors: <String, dynamic>{'exception': e.type.toString()},
);
}
/// Helper to parse the errors map from various possible formats.
Map<String, dynamic> _parseErrors(dynamic errors) {
if (errors is Map) {
return Map<String, dynamic>.from(errors);
}
return const <String, dynamic>{};
}
}

View File

@@ -21,3 +21,4 @@ dependencies:
flutter_bloc: ^8.1.0 flutter_bloc: ^8.1.0
equatable: ^2.0.8 equatable: ^2.0.8
flutter_modular: ^6.4.1 flutter_modular: ^6.4.1
dio: ^5.9.1

View File

@@ -6,6 +6,9 @@
/// Note: Repository Interfaces are now located in their respective Feature packages. /// Note: Repository Interfaces are now located in their respective Feature packages.
library; library;
// Core
export 'src/entities/core/services/api_service/api_response.dart';
// Users & Membership // Users & Membership
export 'src/entities/users/user.dart'; export 'src/entities/users/user.dart';
export 'src/entities/users/staff.dart'; export 'src/entities/users/staff.dart';

View File

@@ -0,0 +1,22 @@
/// Represents a standardized response from the API.
class ApiResponse {
/// Creates an [ApiResponse].
const ApiResponse({
required this.code,
required this.message,
this.data,
this.errors = const <String, dynamic>{},
});
/// The response code (e.g., '200', '404', or custom error code).
final String code;
/// A descriptive message about the response.
final String message;
/// The payload returned by the API.
final dynamic data;
/// A map of field-specific error messages, if any.
final Map<String, dynamic?> errors;
}