When developing Flutter applications, managing the state and ensuring efficient API calls are crucial for building scalable and maintainable apps. A design pattern that can facilitate this achievement is the Singleton Pattern. In this blog, we’ll explore what the Singleton Pattern is, why it’s useful, and how to implement it in Flutter for making GET, PUT, POST, PATCH, and DELETE API calls.
What is the Singleton Pattern?
The Singleton Pattern is a design pattern that ensures a class has only one instance and Offers a centralized access point to the instance, making it especially beneficial in situations where
you need a single point of control, such as managing API calls, database connections, or shared resources.
In Flutter, the Singleton Pattern is often used to create a single instance of an API service class, Guaranteeing that every part of the app utilizes the same instance for handling network requests.
Why Use the Singleton Pattern in Flutter?
Singleton Instance: Guarantees that only one API service instance is created, avoiding redundancy. unnecessary resource consumption.
Universal Access: Offers a single access point to the API service, ensuring seamless usage throughout the app.
Consistency: Maintains consistent state and behavior throughout the app.
Efficiency: Reduces the overhead of creating multiple instances of the same service. Implementing the Singleton Pattern in Flutter
Let’s create a Singleton class for managing API calls in Flutter. We’ll use the http package for making network requests.
Step 1: Add Dependencies
Include the http package in your pubspec.yaml file under dependencies.
dependencies:
flutter:
sdk: flutter http: ^0.15.0
Step 2: Create the Singleton Class
Create a file named api_service.dart and define the Singleton class:
import 'package:http/http.dart' as http;
import 'dart:convert';
class ApiService {
// Private constructor ApiService._internal();
// Static instance of the class
static final ApiService _instance = ApiService._internal();
// Factory constructor to provide the instance factory ApiService() {
return _instance;
}
// Base URL of the API
static const String _baseUrl = 'https://jsonplaceholder.typicode.com';
// HTTP client
final http.Client _client = http.Client();
// GET request
Future<dynamic> get(String endpoint) async {
final response = await _client.get(Uri.parse('$_baseUrl/$endpoint')); return _handleResponse(response);
}
// POST request
Future<dynamic> post(String endpoint, dynamic body) async { final response = await _client.post(
Uri.parse('$_baseUrl/$endpoint'),
headers: {'Content-Type': 'application/json'}, body: jsonEncode(body),
);
return _handleResponse(response);
}
// PUT request
Future<dynamic> put(String endpoint, dynamic body) async { final response = await _client.put( Uri.parse('$_baseUrl/$endpoint'),
headers: {'Content-Type': 'application/json'}, body: jsonEncode(body),
);
return _handleResponse(response);
}
// PATCH request
Future<dynamic> patch(String endpoint, dynamic body) async { final response = await _client.patch( Uri.parse('$_baseUrl/$endpoint'),
headers: {'Content-Type': 'application/json'}, body: jsonEncode(body),
);
return _handleResponse(response);
}
// DELETE request
Future<dynamic> delete(String endpoint) async {
final response = await _client.delete(Uri.parse('$_baseUrl/$endpoint')); return _handleResponse(response);
}
// Handle the HTTP response
dynamic _handleResponse(http.Response response) { if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Failed to load data: ${response.statusCode}');
}
}
}
Step 3: Use the Singleton Class in Your App
Now that the Singleton class is set up, you can use it to make API calls from anywhere in your app.
Example: Fetching Data (GET Request)
class HomePage extends StatelessWidget {
final ApiService _apiService = ApiService();
Future<void> fetchData() async { try {
final data = await _apiService.get('posts/1'); print('Fetched Data: $data');
} catch (e) { print('Error: $e');
}
}
@override
Widget build(BuildContext context) { return Scaffold(
appBar: AppBar(title: Text('Singleton Pattern Example')), body: Center(
child: ElevatedButton( onPressed: fetchData, child: Text('Fetch Data'),
),
),
);
}
}
Example: Creating Data (POST Request)
Future<void> createPost() async {
final newPost = { 'title': 'New Post',
'body': 'This is a new post created using the Singleton Pattern.', 'userId': 1,
};
try {
final response = await _apiService.post('posts', newPost); print('Created Post: $response');
} catch (e) { print('Error: $e');
}
}
Example: Updating Data (PUT Request)
Future<void> updatePost() async {
final updatedPost = { 'id': 1,
'title': 'Updated Post',
'body': 'This post has been updated using the Singleton Pattern.', 'userId': 1,
};
try {
final response = await _apiService.put('posts/1', updatedPost); print('Updated Post: $response');
} catch (e) { print('Error: $e');
}
}
Example: Partially Updating Data (PATCH Request)
Future<void> patchPost() async {
final patchData = { 'title': 'Patched Post',
};
try {
final response = await _apiService.patch('posts/1', patchData); print('Patched Post: $response');
} catch (e) { print('Error: $e');
}
}
Example: Partially Updating Data (PATCH Request)
Future<void> patchPost() async {
final patchData = { 'title': 'Patched Post',
};
try {
final response = await _apiService.patch('posts/1', patchData); print('Patched Post: $response');
} catch (e) { print('Error: $e');
}
}
Example: Deleting Data (DELETE)
Request) Future<void> deletePost() async {
try {
final response = await _apiService.delete('posts/1'); print('Deleted Post: $response');
} catch (e) { print('Error: $e');
}
}
Conclusion
The Singleton Pattern is a powerful tool for managing API services in Flutter. By ensuring a single instance of the API service, you can maintain consistency, reduce resource consumption, and simplify your codebase. With the examples provided, you can now implement GET, POST, PUT, PATCH, and DELETE API calls in your Flutter app using the Singleton Pattern.
Top comments (0)