I'm working on a Flutter project where I make multiple API calls using the http package. Right now, I'm writing separate get and post methods in different files, which is causing code duplication and inconsistency.
I want to build a single, reusable class that:
Manages a base URL
Handles GET and POST requests
Supports optional headers and JSON encoding
Returns parsed responses
Can be extended easily to support other HTTP methods
Add http
dependency to pubspec.yaml
dependencies:
http: ^0.13.6
2. Create a file: api_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiService {
final String baseUrl;
ApiService({required this.baseUrl});
Future<dynamic> get(String endpoint, {Map<String, String>? headers}) async {
final url = Uri.parse('$baseUrl$endpoint');
try {
final response = await http.get(url, headers: headers);
return _handleResponse(response);
} catch (e) {
throw Exception('GET request error: $e');
}
}
Future<dynamic> post(String endpoint, {Map<String, String>? headers, dynamic body}) async {
final url = Uri.parse('$baseUrl$endpoint');
try {
final response = await http.post(
url,
headers: headers ?? {'Content-Type': 'application/json'},
body: json.encode(body),
);
return _handleResponse(response);
} catch (e) {
throw Exception('POST request error: $e');
}
}
dynamic _handleResponse(http.Response response) {
final statusCode = response.statusCode;
final body = response.body.isNotEmpty ? json.decode(response.body) : null;
if (statusCode >= 200 && statusCode < 300) {
return body;
} else {
throw Exception('Error ${response.statusCode}: ${response.reasonPhrase}');
}
}
}
3. Usage Example:
void main() async {
final api = ApiService(baseUrl: 'https://jsonplaceholder.typicode.com/');
// GET request
try {
final posts = await api.get('posts');
print(posts);
} catch (e) {
print(e);
}
// POST request
try {
final newPost = await api.post('posts', body: {
'title': 'foo',
'body': 'bar',
'userId': 1,
});
print(newPost);
} catch (e) {
print(e);
}
}