The factory pattern in Dart is a very elegant way to control object creation. It allows you to decide how and when an instance of a class should be created, instead of always returning a new object directly from a constructor.
In Dart, you can implement this pattern using a factory constructor. The key difference between a normal constructor and a factory constructor is that a factory constructor can return an existing instance, a subclass, or even null, instead of always creating a new instance.
Let me give you a simple example first:
class DatabaseConnection {
static final DatabaseConnection _instance = DatabaseConnection._internal();
// Private named constructor
DatabaseConnection._internal();
// Factory constructor returns the same instance
factory DatabaseConnection() {
return _instance;
}
void connect() => print("Connected to database");
}
void main() {
var db1 = DatabaseConnection();
var db2 = DatabaseConnection();
print(db1 == db2); // true → both point to the same instance
}
Here’s what’s happening —
- The
_internal()constructor is private, so you can’t create new instances from outside. - The
factoryconstructor returns a single shared instance — this is basically the Singleton pattern, implemented using a factory constructor. - Every time you call
DatabaseConnection(), you get the same object back, ensuring consistent connection management.
In one of my projects, I applied this pattern for an API client manager. I didn’t want multiple HTTP client instances across different screens because that could lead to redundant network connections. Using a factory constructor made sure the client was initialized once and reused everywhere.
The factory pattern is also great when you want to decide which subclass to return based on a condition. For example:
abstract class Shape {
void draw();
}
class Circle implements Shape {
void draw() => print("Drawing Circle");
}
class Square implements Shape {
void draw() => print("Drawing Square");
}
class ShapeFactory {
factory ShapeFactory(String type) {
if (type == 'circle') return Circle();
if (type == 'square') return Square();
throw Exception("Unknown shape type");
}
}
The factory constructor decides which class to instantiate — you just request a shape type, and it gives you the right object.
A challenge I faced early on was remembering that factory constructors don’t have access to this, since they don’t necessarily create a new instance. You can only return objects — not initialize instance variables directly inside them.
A limitation is that it can slightly obscure where instances actually come from if overused. So, I use the factory pattern mainly when:
- I need a single shared instance (Singleton)
- I want conditional object creation (like returning subclasses)
- Or when object creation is expensive, and I want to cache or reuse objects.
So, to summarize —
The factory pattern in Dart helps control how instances are created, allowing caching, conditional instantiation, or singletons. Using the factory constructor makes code more flexible, efficient, and maintainable — especially in large apps with many shared resources.
