We’ve all encountered the theoretical concepts of design patterns, from the classic Gang of Four to modern approaches. We’ve read about their benefits in improving code structure, reusability, and maintainability. However, the true test lies in their practical application.
Are we truly integrating these design patterns into our daily Flutter development? Let’s explore some concrete examples of design pattern implementations in Flutter.
Factory Method
The factory method design pattern in Flutter is an excellent approach to creating objects where the exact types of object to be instantiated is determined at runtime or the subclass needs to decide.
When to use:
- Platform Specific Implementation: Creating different objects based on the platform.
- Dynamic Creation: When the object type needs to be decided at the runtime based on the specific condition.
- Decoupling Object Creation: Separating the logic from the usage, promoting modular and maintainable code.
Let’s See It in Action:
Let’s think of creating widgets based on the platform or having logic based on the platform. If the platform is iOS we will show a design, for Android and iOS we will show a different UI.
When lambdas are passed around frequently, such as in loops or performance-sensitive code, this overhead can become significant. Kotlin solves this problem using inline functions.



In this example, ButtonWidgetFactory is an abstract class with a createWidget method. The RectanlgeBorderButton and RoundedBorderButton are concrete implementations creating platform specific widgets.

Image: Different Implementation in Different OS
Singleton
The singleton design pattern is one of the most commonly used design patterns in software architecture. It ensures that a class has only one instance, providing a global point of access for that instance.
When to use:
While the Singleton pattern is often used for managing shared resources like NetworkClients, Databases, and Configurations, it’s less common to employ it for creating widgets in Flutter. Given the immutability of Flutter widgets, using a Singleton might be considered overkill in most cases.
However, there are specific scenarios where the Singleton pattern can be useful for widget management:
- Global Theaming: A Singleton can centralize theme configurations, allowing you to easily switch themes across your entire app.
- Configuration Management: Managing settings and configuration that are global to the entire application
- Database Connection Pool: To avoid data racing and to avoid corruption on disk read write operation we use Singleton.
- Logging: As logging service is accessible across the application we use singleton.
- State Management: Holding application-wide state, like user sessions.
Let’s See It in Action:

Testing Singleton in Flutter:
Testing a Singleton can be challenging due to its global state. However, you can make the SIngleton pattern more testable by designing the singleton with testing in mind, such as allowing injection to the dependencies. Here’s an example:

If we run the test:

Builder Pattern
The Builder pattern is a helpful way to build complex objects step by step. It lets you choose which parts to include and how to assemble them. This is useful when you have many options or want to create different versions of the same object. It also allows the same construction process to create different representations.
When to use:
- Multiple Optional Parameter: When a class has numerous optional parameters, the constructor can become cluttered and difficult to read.
- Step-by-step Construction: In situations where you need to create a widget step by step, builder pattern can come handy.
- Flexible Customization: The builder pattern can be used to create different representation of the same object without modifying the core class
Let’s See It in Action:

And the implementation:
final trxCellWithoutFee =
TrxCellBuilder()
.setAmount(500.0)
.setCharge(5.75)
.build();
final trxCellWithOnlyAmount =
TrxCellBuilder()
.setAmount(100.23).build();
runApp(
MaterialApp(
home: Scaffold(
body: SafeArea(
child: Center(
child: Column(
children: [
const SizedBox(height: 120,),
const Text(
'Without Fee',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const SizedBox(
height: 20,
),
trxCellWithoutFee,
const SizedBox(
height: 40,
),
const Text(
'Without Fee and Charge',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.red,
),
),
const SizedBox(
height: 20,
),
trxCellWithOnlyAmount
],
),
),
),
),
),
);
This is how it would look into the UI, we are having a different representation of the same object

Bonus:
The prototype design pattern
The ProtoType Design Pattern is a creational design pattern that allows us to create new objects by cloning existing objects.
When to use:
- When we want a create a new object that are similar to existing object but with one or more than one different properties
Let’s See It in Action:

Happy coding, developers! 🙂