← Flutter & Dart

Advanced Dart

What kinds of constructors exist in Dart (default, named, `factory`) and what is each one for?

Show answer — try answering out loud first

Dart has the default constructor (the one with the class name), named constructors (to have several ways of creating the object), and the factory constructor (which can decide which instance to return, even an existing one). On top of that, there's syntactic sugar for assigning properties directly from the parameters.

  • Default constructor: the classic one. Dart lets you shorten the assignment with this.property in the parameters.
  • Named constructor (Class.name()): useful for creating the object in different ways (for example, Point.origin() or Color.fromHex()).
  • factory: it's not required to create a new instance. It can return a cached one, a subclass, or build from another source. It's used a lot in singleton patterns and in fromJson.
  • Initializer list (: field = value): assigns final fields before the constructor body runs.
class Point {
  final double x;
  final double y;

  // Default constructor with syntactic sugar
  Point(this.x, this.y);

  // Named constructor
  Point.origin()
      : x = 0,
        y = 0;

  // Factory: decides what to return
  factory Point.fromMap(Map<String, double> map) {
    return Point(map["x"] ?? 0, map["y"] ?? 0);
  }

  @override
  String toString() => "Point($x, $y)";
}

void main() {
  print(Point(3, 4)); // Point(3.0, 4.0)
  print(Point.origin()); // Point(0.0, 0.0)
  print(Point.fromMap({"x": 1, "y": 2})); // Point(1.0, 2.0)
}

Confusing a normal constructor with a factory. A normal constructor always creates a new instance and can't have a return. A factory must have a return and can return an already existing instance. Trying to put a return in a normal constructor is a classic error.

"Dart has the default constructor, which can shorten the assignment with this.x in the parameters; named constructors like Point.origin() to have several ways of creating the object; and the factory constructor, which unlike a normal one isn't required to create a new instance: it can return a cached one or build from a JSON. That's why factory is common in singletons and in the fromJson of models."

Quick challenge

Why is factory typically used to implement a singleton?

See answer

Because a factory can always return the same instance instead of creating a new one. You store a private static instance and the factory returns it every time:

class Config {
  static final Config _instance = Config._internal();
  factory Config() => _instance;
  Config._internal();
}

Every Config() returns the same object.