DEV Community

Cover image for Singleton Pattern in Flutter: A Comprehensive Guide with API Examples
Tech Tales
Tech Tales

Posted on

Singleton Pattern in Flutter: A Comprehensive Guide with API Examples

Image description

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
Enter fullscreen mode Exit fullscreen mode

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}');
}

}
}

Enter fullscreen mode Exit fullscreen mode

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'),
),
),
);
}
}

Enter fullscreen mode Exit fullscreen mode

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');
}
}

Enter fullscreen mode Exit fullscreen mode

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');
}
}

Enter fullscreen mode Exit fullscreen mode

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');
}
}

Enter fullscreen mode Exit fullscreen mode

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');
}
}

Enter fullscreen mode Exit fullscreen mode

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');
}
}

Enter fullscreen mode Exit fullscreen mode

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.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay