What is a Constructor
A constructor is a special function used to create class instances, typically for initializing properties. Except for the default constructor, all constructors share the same name as their class.
Dart Constructor Types:
- Generative Constructors
- Default Constructors
- Named Constructors
- Const Constructors
- Redirecting Constructors
- Factory Constructors
- Redirecting Factory Constructors
- Concise Constructor Syntax (Dart 3.13+)
- Primary Constructors (Minimal field + constructor syntax)
1. Generative Constructor
The most basic constructor, which creates instances and initializes member variables. It supports initializer formal parameters this.variable to simplify assignment logic.
class Point {
double x;
double y;
// Generative constructor: this.x directly assigns values to class members
Point(this.x, this.y); // Directly assign values to x and y
}
void main() {
final p = Point(10, 20);
print("(${p.x}, ${p.y})"); // (10, 20)
}
Code language: JavaScript (javascript)
2. Default Constructor
If no custom constructor is manually defined for a class, Dart automatically provides a parameterless, unnamed generative constructor. This behavior is identical to other C-like languages including C++, Java, C#, etc.
class PointA {
double x = 0;
double y = 0;
// Implicit default constructor: PointA();
}
void main() {
final p = PointA(); // Automatically calls the default constructor
print("(${p.x}, ${p.y})"); // (0.0, 0.0)
}
Code language: PHP (php)
Note: The default constructor disappears as soon as you write any custom constructor manually.
3. Named Constructor
Purpose: Define multiple constructors within one class with clear semantic meanings. Syntax format: ClassName.ConstructorName()
const double xOrigin = 0;
const double yOrigin = 0;
class Point {
final double x;
final double y;
// Primary generative constructor
Point(this.x, this.y);
// Named constructor: origin point
Point.origin() : x = xOrigin, y = yOrigin;
}
void main() {
final p1 = Point(5, 6);
final p2 = Point.origin(); // Call named constructor
print(p1.x); //5
print("Origin: (${p2.x}, ${p2.y})"); // (0,0)
}
Code language: PHP (php)
Key Rules
- Subclasses do not inherit parent named constructors; you must manually implement matching named constructors in subclasses if needed;
- Named constructors support initializer lists to assign values to final fields.
4. Const Constructor
Used to create compile-time constant objects, with the following requirements:
- All member variables must be marked
final - Prefix the constructor with
const. Useconstat instantiation to reuse cached constant instances; omitting const creates a regular separate object.
class ImmutablePoint {
static const ImmutablePoint origin = ImmutablePoint(0, 0); // Static class-wide shared singleton
final double x, y;
// Const constructor initializes final x and y
const ImmutablePoint(this.x, this.y);
}
void main() {
// Compile-time constant, reuses the same object reference
const p1 = ImmutablePoint(1, 2);
const p2 = ImmutablePoint(1, 2); // Identical parameters share the same instance
print(p1 == p2); // true
// Regular instance, brand-new separate object
final p3 = ImmutablePoint(1, 2);
print(p1 == p3); // false
}Code language: PHP (php)
5. Redirecting Constructor
A constructor that forwards execution to another constructor of the same class using : this(arguments). The constructor body must be empty.
class Point {
double x, y;
// Primary constructor
Point(this.x, this.y);
// Redirecting constructor: only pass x, fix y to 0 and forward to main constructor
Point.alongX(double x) : this(x, 0); // Invoke main constructor with fixed y=0
}
void main() {
final p = Point.alongX(9);
print("x=${p.x}, y=${p.y}"); // x=9, y=0
}
Code language: JavaScript (javascript)
6. Factory Constructor
Marked with the factory keyword, applicable scenarios:
- Not guaranteed to create new objects (cache reuse, return subclass instances)
- Run complex pre-initialization logic (JSON parsing, parameter validation). Restriction: cannot access
this
Singleton Cache Logger Example
class Logger {
final String name;
bool mute = false;
// Private cache library-scoped
static final Map<String, Logger> _cache = {};
// Factory constructor: retrieve from cache first
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
// Factory constructor: build instance from JSON
factory Logger.fromJson(Map<String, dynamic> json) {
return Logger(json["name"].toString());
}
// Private internal generative constructor
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
void main() {
final log1 = Logger("UI");
final log2 = Logger("UI");
print(log1 == log2); // true, same cached object
final jsonLog = Logger.fromJson({"name": "Net"});
jsonLog.log("request success");
}
Code language: PHP (php)
7. Redirecting Factory Constructor
Syntax: factory Name() = OtherClass.Constructor;. Ideal for abstract classes delegating instance creation to avoid repetitive parameter definitions.
abstract class Listenable {}
class _MergingListenable implements Listenable {
List<_MergingListenable> list;
_MergingListenable(this.list);
}
// Redirect factory to internal private class constructor
factory Listenable.merge(List<Listenable> list) = _MergingListenable;
Code language: PHP (php)
8. Constructor Tear-offs
Pass constructors directly as function arguments without parentheses, replacing redundant lambda anonymous functions for better performance.
Recommended Style
void main() {
final charCodes = [65, 66, 67];
// Named constructor String.fromCharCode
final strs = charCodes.map(String.fromCharCode);
print(strs); // (A, B, C)
// Unnamed constructor tear-off via .new
final buffers = charCodes.map(StringBuffer.new);
}
Code language: PHP (php)
Discouraged Redundant Lambda
// Unnecessary wrapping, avoid this pattern
charCodes.map((code) => String.fromCharCode(code));
Code language: JavaScript (javascript)
9. Concise Constructor Syntax (Dart 3.13+)
Omit the class name inside the class body; use new / factory directly to define constructors without ClassName.xxx:
| Traditional Syntax | Shortened Syntax |
|---|---|
Point(this.x,this.y) | new(this.x,this.y) |
Point.origin():x=0,y=0 | new origin():x=0,y=0 |
factory Point.clone() | factory clone() |
class Point {
double x, y;
// Shortened unnamed generative constructor
new(this.x, this.y);
// Shortened named generative constructor
new origin() : x = 0, y = 0;
// Shortened factory constructor
factory clone(Point other) => new(other.x, other.y);
}
void main() {
final p = new(3, 4);
final origin = new origin();
final copy = p.clone(p);
}
Code language: PHP (php)
10. Three Ways to Initialize Instance Variables
1: Direct assignment at declaration
class PointA {
double x = 1.0;
double y = 2.0;
}
Code language: JavaScript (javascript)
2: Initializer Formal Parameters
this.variable
Supports optional positional parameters, named parameters, and nullable fields
class PointB {
final double x;
final double y;
// Optional positional parameters
PointB.opt([this.x = 0, this.y = 0]);
// Named parameters with default values
PointB.named({this.x = 5, this.y = 5});
}
Code language: PHP (php)
3: Initializer List
Assign values after colon :. Executes before the constructor body. Cannot reference this. Supports assert for parameter validation.
class Point {
final double x;
final double y;
Point.fromJson(Map<String, double> json)
: x = json["x"]!,
y = json["y"]! {
print("Constructor body executed, x=$x");
}
// With assertion validation
Point.withAssert(this.x, this.y) : assert(x >= 0);
}
Code language: PHP (php)
11. Direct Private Field Initializer Parameters (Dart 3.12+)
For underscore-prefixed private fields, write constructor parameters as this._field. External callers use the public name without underscore.
class Point {
final double _x;
final double _y;
// Internal storage _x, external invocation uses x:10
Point({required this._x, this._y = 0});
}
void main() {
final p = Point(x: 10); // No underscore required for external calls
print(p._x); // 10
}
Code language: PHP (php)
Use original private variable names inside initializer lists:
class Point {
final double _x;
Point({required this._x}) : assert(_x > 0);
}
Code language: JavaScript (javascript)
12. Parent Class Constructors & Inheritance Rules
- Subclasses do not inherit any parent constructors;
- Constructor execution order: Initializer list → Superclass constructor → Subclass constructor body;
- If the superclass lacks a parameterless default constructor, subclasses must manually call
super()to specify the parent constructor.
Basic super Call Example
Similar to Java’s super keyword; C# uses base for base class references.
class Person {
final String name;
Person(this.name);
Person.fromMap(Map map) : name = map["name"];
}
class Employee extends Person {
int id;
// Call parent named constructor
Employee(Map data) : super.fromMap(data), id = data["id"];
}
Code language: PHP (php)
Note:
thiscannot be used inside super argument expressions.
Super Parameters (Dart 2.17+, simplify parent argument forwarding)
Use super.variable to forward arguments directly to the superclass constructor without manual super(x,y)
class Vector2d {
final double x, y;
Vector2d(this.x, this.y);
}
class Vector3d extends Vector2d {
final double z;
// super.x super.y auto-forward to parent constructor
Vector3d(super.x, super.y, this.z);
}
// Parent named constructor + super named parameters
class Vector3dYZ extends Vector2d {
final double z;
Vector3dYZ({required super.y, required this.z}) : super.named(x: 0);
}
Code language: JavaScript (javascript)