Sparround

Constructors

In Dart a constructor is not just "a function that makes an object" — it is one of the richest parts of the language, and it comes up in almost every interview.

The main forms:

  • Default (generative) constructorPoint(this.x, this.y);. The this.x shorthand assigns the parameter straight into the field, with no extra line in the body.
  • Named constructorPoint.origin() : x = 0, y = 0;. Dart has no constructor overloading (you cannot declare two constructors with the same name), so every alternative way of building the object gets a name.
  • Initializer list — the part after : that runs before the body.
  • Redirecting constructorPoint.zero() : this(0, 0); — forwards the work to another constructor of the same class and has no body of its own.
  • Factory constructor — does not have to create a new object: it may return a cached instance, a subtype, or any suitable instance other than null.
  • Const constructor — builds the object at compile time and canonicalises objects with equal values.

When is an initializer list REQUIRED? This is the classic form of the question. Three cases:

  • You need to fill a final field with a computed value — you cannot assign to a final in the body, because by the time the body runs the object counts as fully built.
  • You need to pass a computed argument to super(...).
  • You want to validate input with assert(...) before the object exists.

The execution order (a list worth memorising):

1. The initializer list, left to right — you may not use this here 2. The superclass constructor 3. Your own body — this is available from here on

Constructor kindCreates a new object?Typical use
Generative — `Order(this.id)`Yes, alwaysPlain data classes and models
Named — `Order.draft()`YesAlternative construction scenarios (the overload substitute)
Redirecting — `Order.empty() : this('')`Yes (through another constructor)Keeping default values in one place
Factory — `factory Order.fromJson(...)`No — may return a cache entry, a singleton, or a subtypeJSON parsing, caching, singletons, choosing a subtype
Const — `const Point(this.x, this.y)`Once at compile time, then reusedImmutable value objects

Const constructors and canonicalisation. To make a constructor const, every field must be final, the body must be empty, and the initializer list may only use compile-time constants.

After that, writing const Point(1, 2) twice gives you one single object in the program (identical(a, b) == true). That is canonicalisation. Note the flip side: a class with a const constructor can still be called without const, and then you get an ordinary runtime object.

Why this matters in Flutter: when widgets are created with a const constructor, the framework sees on rebuild that the old and the new object are identical and skips rebuilding that subtree. So const is not a style preference — it is a measurable performance win. That is exactly why the prefer_const_constructors lint exists.

Interview tip. The most frequent question here: "what is the difference between a factory constructor and a normal one?" A weak answer is "a factory creates the object". A strong answer has three beats: (1) a generative constructor must create a fresh instance and implicitly returns this; a factory carries a return obligation instead — what it returns is its own business (cache, singleton, subtype); (2) because of that a factory has no this and cannot have an initializer list; (3) a factory cannot be const, so a class whose only constructor is a factory cannot be used in a const context — a real constraint on Flutter widgets. Then give one concrete example: factory Currency(String code) returning a cached instance.

📚 Sources and documentation