Design Patterns in Flutter
Durga jayasai Pillagolla
Building WorkWave || Student at KL University || Student Peer Mentor || Advisor at Kognitiv club
Singleton Pattern
A single instance of a class is ensured by the Singleton Pattern, which provides a global point of access to it. This comes in handy when you need to share a resource, such as a database connection or configuration, between several areas of the application.
?? Use Case: When you want to ensure only one instance of a class exists, like for a logging system or database connection.
class Singleton {
static final Singleton _instance = Singleton._internal();
factory Singleton() {
return _instance;
}
Singleton._internal();
}
Provider Pattern
Instead of relying on InheritedWidgets, you may easily transfer data down the widget tree with the help of the widely used Provider Pattern, a Flutter state management solution. The dependency injection principle serves as its foundation.
?? Use Case: Manage state across multiple screens or widgets with minimal boilerplate.
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
Composite Pattern
Using the composite pattern, a structural design pattern, you may combine different elements to create tree structures and manipulate them as separate things. It works especially well with recursive user interface elements like menus and trees.
?? Use Case: When your app deals with hierarchical data like folder structures or complex UI layouts.
abstract class Component {
void render();
}
class Leaf implements Component {
String name;
Leaf(this.name);
@override
void render() {
print("Leaf: $name");
}
}
MVVM (Model-View-ViewModel)
In Flutter, MVVM is a well-liked architecture paradigm for keeping UI and business logic apart. Serving as a mediator between the view and the model, the ViewModel responds to user input and exposes data to the view.
?? Use Case: Create clean separation between UI and business logic to improve testability and maintainability.
class UserViewModel extends ChangeNotifier {
User _user = User("John Doe");
String get userName => _user.name;
void updateUserName(String name) {
_user.name = name;
notifyListeners();
}
}
Builder Pattern
Complex objects are built component by component using the Builder Pattern. It allows you to use the same construction code to create different representations of an object by separating the building process from the finished product.
?? Use Case: Build complex objects or UIs in a step-by-step manner.
领英推荐
class HouseBuilder {
String roof;
String walls;
String doors;
House build() {
return House(roof: roof, walls: walls, doors: doors);
}
}
MVC (Model-View-Controller)
Through Model-View-Controller (MVC), a program is divided into three primary parts. As an intermediate, the controller processes data and updates the view in accordance with what the model stores and the view displays.
?? Use Case: When you want a clean separation of concerns between your business logic, UI, and data handling.
class Model {
String data = "Hello, MVC!";
}
class Controller {
final Model model;
final View view;
Controller(this.model, this.view);
}
Factory Pattern
Creating objects without disclosing the creation logic to the client can be done with the help of the factory pattern. Although subclasses have the ability to modify the type of object that is generated, it defines a standard interface for object creation.
?? Use Case: Simplify object creation and centralize logic for creating various object types.
abstract class Shape {
void draw();
}
class ShapeFactory {
static Shape createShape(String type) {
switch (type) {
case 'Circle':
return Circle();
case 'Square':
return Square();
}
}
}
BLoC Pattern
The Bloc Pattern, short for Business Logic Component, is a powerful reactive pattern. It uses Streams to manage and emit states, ensuring a clear separation between business logic and UI.
?? Use Case: Separate your app's business logic from its UI for better testability, scalability, and state management.
class CounterBloc {
final _counterController = StreamController<int>();
int _counter = 0;
Stream<int> get counterStream => _counterController.stream;
void increment() {
_counter++;
_counterController.sink.add(_counter);
}
}
?? Conclusion
Design patterns are the backbone of building scalable, maintainable, and efficient applications. Understanding and applying these patterns in your Flutter projects will not only make your code more readable but also easier to manage and extend in the future. Happy coding! ???
Please let me know if you need any tweaks!