What is a factory constructor?

Medium PriorityAsked in ~55% of Flutter interviews

3 min read

OOP in Dart

Regular constructorFactory constructor
Creates a fresh instance✅ Always❓ Up to you
Can return an existing object
Can return a subtype
Has access to this
Can be const✅ (since Dart 2.0, with restrictions)
Can be async❌ (use a static method instead)

Mental model: a regular constructor is new. A factory constructor is a static method dressed up as a constructor, with one rule — it must return an instance of the declaring type.


Code in action

// 1️⃣ Singleton
class Logger {
  Logger._();                                 // private constructor
  static final Logger _instance = Logger._();
  factory Logger() => _instance;              // always returns the same one
}

identical(Logger(), Logger());                // true

// 2️⃣ Pick a subtype from input
abstract class Shape {
  factory Shape(String kind) => switch (kind) {
    'circle' => Circle(),
    'square' => Square(),
    _        => throw ArgumentError('Unknown: $kind'),
  };
  double get area;
}

// 3️⃣ JSON deserialisation — the most common use in Flutter apps
class User {
  final String id, name;
  User({required this.id, required this.name});

  factory User.fromJson(Map<String, dynamic> json) =>
      User(id: json['id'] as String, name: json['name'] as String);
}

// 4️⃣ Instance cache
class Country {
  final String code;
  Country._(this.code);
  static final _cache = <String, Country>{};
  factory Country(String code) =>
      _cache.putIfAbsent(code, () => Country._(code));
}

identical(Country('IN'), Country('IN'));      // true

When to reach for a factory

SituationWhy
Singleton (logger, DB client, analytics)Return the same instance every call
fromJson / fromMap / fromBytesParsing logic before construction
Pick a subtype based on inputHide concrete classes behind one entry point
Cache expensive instances by keyAvoid redundant allocation/setup
Validate before constructing (and throw on bad input)Regular constructors can't return

If you don't need any of the above, a regular constructor is simpler — don't reach for factory by default.


Common mistakes to avoid

// ❌ Using factory when a regular constructor would do
class Point {
  final int x, y;
  factory Point(int x, int y) => Point._(x, y);   // pointless indirection
  Point._(this.x, this.y);
}
// ✅ Just write a regular constructor

// ❌ Forgetting that factory has no `this`
class User {
  final String id;
  factory User.empty() {
    this.id = '';                                 // ❌ no `this` here
    return User._('');
  }
  User._(this.id);
}

// ❌ Trying to make a factory async
factory User.fromApi() async { ... }              // ❌ not allowed
// ✅ Use a static method instead
static Future<User> fromApi() async { ... }

// ❌ Returning a totally unrelated type
abstract class Shape {
  factory Shape() => 42;                          // ❌ must return Shape (or subtype)
}

// ❌ Singleton without a private constructor — anyone can still call `new`
class Logger {
  static final _i = Logger();
  factory Logger.instance() => _i;
  Logger();                                       // ❌ public — bypasses the factory
}
// ✅ Make the real constructor private (Logger._())

Interview follow-ups

  1. What's the difference between a factory constructor and a static method that returns an instance? Functionally similar. Factory constructors are invoked like constructors (User.fromJson(...), const Foo()), can participate in const evaluation, and the type system treats the result as the declared type. Static methods are more flexible (e.g. async) but feel less idiomatic for instance creation.

  2. Can a factory constructor be const? Yes, if it returns a compile-time constant — typically a const instance of the same class. The factory body has to be a single return of a const expression. Useful for canonicalising values.

  3. How does a factory help implement the Singleton pattern correctly in Dart? You pair it with a private constructor (Logger._()) and a static final instance. The factory hides instantiation, and the private constructor prevents anyone outside the file from calling new directly.

  4. Why can't a factory constructor be async? Constructors must return the type synchronously. async would force the return type to be Future<T>, which doesn't match the constructor contract. The Dart-idiomatic solution is a static method that returns a Future<T>.

How helpful was this content?

Please sign in to rate this article.