Explain named constructors in Dart

Low PriorityAsked in ~40% of Flutter interviews

3 min read

OOP in Dart

Because Dart doesn't support method overloading, it uses named constructors as self-documenting alternative entry points — Point.origin(), EdgeInsets.all(8), Color.fromARGB(...). Reach for them when argument types alone don't convey what a constructor does.

Constructor flavourSyntaxWhen to use
Default / primaryPoint(this.x, this.y);The one "canonical" way to build it
NamedPoint.origin() : x = 0, y = 0;Alternative recipes (fromJson, polar, zero)
RedirectingPoint.square(double s) : this(s, s);Forward to another constructor in the same class
Factoryfactory User.fromJson(...) => ...;Need to choose / cache / validate before building

Every named constructor uses initialiser lists (: x = ..., y = ...) to set final fields before the body runs.


Code in action

class Point {
  final double x, y;

  // Primary
  const Point(this.x, this.y);

  // Named — descriptive alternatives
  const Point.origin() : x = 0, y = 0;

  Point.fromJson(Map<String, dynamic> j)
      : x = (j['x'] as num).toDouble(),
        y = (j['y'] as num).toDouble();

  Point.polar(double r, double theta)
      : x = r * cos(theta),
        y = r * sin(theta);

  // Redirecting — reuses Point(x, y) instead of repeating logic
  Point.square(double v) : this(v, v);

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

Point(3, 4);                        // primary
Point.origin();                     // (0, 0)
Point.fromJson({'x': 1, 'y': 2});   // (1, 2)
Point.polar(5, pi / 4);             // polar → cartesian
Point.square(2);                    // (2, 2) via redirect

Where you see them in Flutter

EdgeInsets.all(16);
EdgeInsets.symmetric(horizontal: 12, vertical: 8);
EdgeInsets.only(top: 4);

BorderRadius.circular(8);
BorderRadius.vertical(top: Radius.circular(12));

Color.fromARGB(255, 33, 150, 243);
Color.fromRGBO(33, 150, 243, 1);

Duration(seconds: 1);
Duration(milliseconds: 500);

Each name makes the intent clear at the call site — far better than overloaded positional arguments.


When to add a named constructor

SituationUse
Multiple ways to build the same objectNamed constructor per recipe
Same logic but with a preset argumentRedirecting constructor
Parse / deserialise an external formatFoo.fromJson / Foo.fromMap / Foo.fromBytes
Need to return existing / cached instancefactory (not a regular named constructor)
Just want a clearer name for the only constructorRename or use named parameters instead

Common mistakes to avoid

// ❌ Duplicating logic across named constructors
class User {
  final String name;
  final int age;
  User(this.name, this.age);
  User.guest()  : name = 'Guest', age = 0;
  User.admin()  : name = 'Admin', age = 99;
  // every change to User must be repeated…
}

// ✅ Redirect to the primary constructor
class User {
  final String name;
  final int age;
  User(this.name, this.age);
  User.guest() : this('Guest', 0);
  User.admin() : this('Admin', 99);
}

// ❌ Putting heavy work in the initialiser list
Point.fromApi(String url)
    : x = http.get(Uri.parse(url)).x;       // ❌ can't call async / complex code
// ✅ Use a factory or static method that returns Future<Point>

// ❌ Trying to call a method on `this` before super()
class Box extends Container {
  Box() : super(width: calcWidth());        // ❌ no instance methods yet
}

// ❌ Forgetting a redirecting constructor cannot have a body
Point.zero() : this(0, 0) { print('made'); }  // ❌ body not allowed

Interview follow-ups

  1. Why does Dart not support constructor overloading like Java/Kotlin? Because Dart embraces named constructors and named parameters instead. Color.fromARGB(...) vs Color.fromRGBO(...) is clearer at the call site than two overloads of Color(...).

  2. What's the difference between a redirecting constructor and a factory constructor? Redirecting (Point.square(double s) : this(s, s);) is still a constructor — it constructs a new instance via another constructor in the same class. A factory chooses or builds the instance imperatively and can return an existing one.

  3. Can a named constructor be const? Yes, as long as it initialises all final fields with compile-time constant values in the initialiser list (no body). const Point.origin() : x = 0, y = 0; works.

  4. What is the initialiser list and why is it needed? The colon-separated bit between the parameter list and the body. It runs before super() and the constructor body — the only place where final fields can be assigned and where you can call super(...) / assert(...).

How helpful was this content?

Please sign in to rate this article.