Singleton and Design Patterns in Dart & Flutter
Teqani Blogs
Writer at Teqani
This article explores the application of the Singleton pattern and various design patterns in Dart and Flutter for building robust and maintainable applications. Understanding these patterns is crucial for creating scalable and testable codebases.
Singleton Pattern
The Singleton pattern ensures that a class has only one instance throughout the application lifecycle and provides a global point of access to it. This is commonly used for managing resources like database connections, shared preferences, or configuration settings.
Example:
class DatabaseService {
DatabaseService._internal();
static final DatabaseService _instance = DatabaseService._internal();
factory DatabaseService() => _instance;
void connect() => print("Database connected");
}
final db = DatabaseService();
db.connect(); // Always refers to the same instance
When to use:
- Database connections
- Shared preferences or local caches
- Logging or configuration services
Common Design Patterns in Flutter
Factory Pattern
The Factory pattern creates objects without exposing the creation logic to the client. This is useful for abstracting the object creation process and providing flexibility in choosing which concrete class to instantiate.
class InvoiceFactory {
static Invoice create(String type) {
if (type == "gst") return GstInvoice();
if (type == "retail") return RetailInvoice();
throw Exception("Invalid type");
}
}
Use Case: Widget factories, model parsing from JSON, or dependency creation.
Provider Pattern (State Management)
The Provider pattern, often used for state management in Flutter, implements the Observer Pattern. Widgets listen to changes in a provider and rebuild automatically. This allows for reactive UI updates and efficient state management.
class LoginProvider extends ChangeNotifier {
bool isLoading = false;
void toggleLoading() {
isLoading = !isLoading;
notifyListeners();
}
}
Use Case: Dynamic UI updates (e.g., login progress, dashboard refresh).
Repository Pattern
The Repository pattern separates data access from business logic. This pattern provides an abstraction layer between your application and the data source, making it easier to switch between different data sources (e.g., APIs, databases, local caches) without affecting the rest of the application.
class InvoiceRepository {
final ApiService api;
InvoiceRepository(this.api);
Future<List<Invoice>> fetchInvoices() => api.getInvoices();
}
Use Case: Clean architecture—makes APIs, DBs, and local caches swappable.
Builder Pattern
The Builder pattern is used when an object requires step-by-step construction. This pattern is useful for creating complex objects with many optional parameters or configurations. It allows you to construct an object incrementally.
class InvoiceBuilder {
String? id;
double? amount;
InvoiceBuilder setId(String id) { this.id = id; return this; }
InvoiceBuilder setAmount(double amount) { this.amount = amount; return this; }
Invoice build() => Invoice(id!, amount!);
}
Use Case: Building complex objects like form submissions or configurations.
Command Pattern
The Command pattern encapsulates actions as objects, making them reusable and undoable. This pattern is useful for implementing features like undo/redo, queuing tasks, or managing button actions.
abstract class Command {
void execute();
}
class PrintCommand implements Command {
@override
void execute() => print("Printing Invoice...");
}
Use Case: Button actions, undo/redo, queued tasks.
Summary Table
| Pattern | Core Idea | Common Use Case |
|---|---|---|
| Singleton | One shared instance | DB, config, logging |
| Factory | Centralized object creation | API parsing, widget factories |
| Provider (Observer) | Reactive state updates | UI state management |
| Repository | Abstract data layer | Clean architecture |
| Builder | Stepwise object creation | Forms, configurations |
| Command | Encapsulate actions | Undo/redo, task queues |
In production Flutter apps, combining Singleton for services, Provider for state, and Repository for data separation gives a clean, testable, and scalable architecture.
All blogs are certified by our company and reviewed by our specialists
Issue Number: #4778d9ea-a663-40e5-b344-e3369d52d5c9