Simplifying API Calls in Flutter
In modern app development, interacting with APIs is crucial for fetching data, updating resources, and performing various operations. Flutter, being a versatile framework, offers powerful tools to handle API requests efficiently. One such tool is the ApiService
class, designed to streamline API interactions across your Flutter projects.
Introducing ApiService
The ApiService
class is a central component that encapsulates API functionalities, making code organization cleaner and API calls consistent throughout the application. It leverages the http
package for network requests and dart:convert
for JSON serialization.
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
class ApiService {
Uri _getUri(String endpoint) {
return Uri.parse('${ApiUrl.baseUrl}$endpoint');
}
String _accessToken() {
return "";
}
Map<String, String> _headers() {
return {
"Content-Type": "application/json",
"token": _accessToken(),
};
}
Future<http.Response> _sendRequest(
Future<http.Response> Function() requestFunc, Uri uri,
{dynamic body}) async {
try {
_logRequest(uri, body);
final response = await requestFunc();
_logResponse(uri, response);
if (_isUnauthorized(response.statusCode)) {
_handleUnauthorizedAccess();
}
return response;
} catch (e) {
log("Error during API call to $uri: $e");
rethrow;
}
}
void _logRequest(Uri uri, dynamic body) {
log("Request URL: $uri");
if (body != null) log("Request Body: ${jsonEncode(body)}");
}
void _logResponse(Uri uri, http.Response response) {
log("Response for URL: $uri");
log("Status Code: ${response.statusCode}");
log("Response Body: ${response.body}");
}
bool _isUnauthorized(int statusCode) {
return statusCode == 401 || statusCode == 403;
}
void _handleUnauthorizedAccess() {
log("Unauthorized access detected.");
}
Future<http.Response> get(String endpoint) async {
Uri uri = _getUri(endpoint);
return _sendRequest(() => http.get(uri, headers: _headers()), uri);
}
Future<http.Response> post(String endpoint, dynamic body) async {
Uri uri = _getUri(endpoint);
return _sendRequest(
() => http.post(uri, headers: _headers(), body: jsonEncode(body)), uri,
body: body);
}
Future<http.Response> put(String endpoint, dynamic body) async {
Uri uri = _getUri(endpoint);
return _sendRequest(
() => http.put(uri, headers: _headers(), body: jsonEncode(body)), uri,
body: body);
}
Future<http.Response> patch(String endpoint, dynamic body) async {
Uri uri = _getUri(endpoint);
return _sendRequest(
() => http.patch(uri, headers: _headers(), body: jsonEncode(body)), uri,
body: body);
}
Future<http.Response> delete(String endpoint, {dynamic body}) async {
Uri uri = _getUri(endpoint);
return _sendRequest(
() => http.delete(uri, headers: _headers(), body: jsonEncode(body)),
uri,
body: body);
}
Future<http.Response> updateProfileImage(String imagePath, endpoint) async {
final uri = _getUri(endpoint);
var request = http.MultipartRequest('PATCH', uri);
request.files.add(await http.MultipartFile.fromPath('image', imagePath));
request.headers.addAll(_headers());
_logRequest(uri, {"imagePath": imagePath});
var streamedResponse = await request.send();
var response = await http.Response.fromStream(streamedResponse);
_logResponse(uri, response);
return response;
}
}
Initializing the ApiService
To start using ApiService
, you need to create an instance of it in your project. Typically, this is done at the initialization stage of your app.
ApiService apiService = ApiService();
GET Request Example
Fetching data from an API endpoint is a common operation. Here’s how you can use ApiService
for a GET request:
Future<void> fetchUserData() async {
final response = await apiService.get('/users');
// Process the response data here…
}
POST Request Example
Creating or updating resources often involves sending data to the server. Let’s see how a POST request can be made using ApiService
:
Future<void> createUser(Map<String, dynamic> userData) async {
final response = await apiService.post('/users', userData);
// Handle the response accordingly...
}
PUT Request Example
When updating existing resources, a PUT request is typically used. Here’s an example of using ApiService
for a PUT request:
Future<void> updateUser(String userId, Map<String, dynamic> updatedData) async {
final response = await apiService.put('/users/$userId', updatedData);
// Process the response as needed...
}
PATCH Request Example
Similar to PUT requests, PATCH requests are used for partial updates. Here’s how you can utilize ApiService
for a PATCH request:
Future<void> updateProfile(String userId, Map<String, dynamic> profileData) async {
final response = await apiService.patch('/users/$userId/profile', profileData);
// Handle the response accordingly...
}
DELETE Request Example
Deleting resources is another common operation. Here’s how you can perform a DELETE request using ApiService
:
Future<void> deleteUser(String userId) async {
final response = await apiService.delete('/users/$userId');
// Process the response as needed...
}
File Upload Example
Uploading files, such as images, is often required in apps. ApiService
can handle file uploads seamlessly. Here's an example:
Future<void> uploadProfileImage(String imagePath, String userId) async {
final response = await apiService.updateProfileImage(imagePath, '/users/$userId/avatar');
// Handle the upload response...
}
The ApiService
class provides a structured approach to handling API requests in Flutter apps. By encapsulating common HTTP methods and providing consistent error handling, it simplifies the integration of backend services with your frontend UI. Whether it's fetching data, updating resources, or uploading files, ApiService
streamlines the communication between your Flutter app and the server, making your codebase more manageable and maintainable.