Primary constructors
Create classes concisely by declaring parameters and fields in the class header.
Overview
#Primary constructors provide a concise way to declare a class's fields and its main constructor in a single line. They reduce the boilerplate of declaring fields, passing parameters, and assigning them in the constructor body. This shorthand is purely "syntax sugar" and does not introduce new runtime semantics.
Before and after
#Consider this traditional class with two fields and a constructor:
// Current syntax.
class Point {
int x;
int y;
Point(this.x, this.y);
}
Using a primary constructor makes the same class much more concise:
// Using a primary constructor.
class Point(var int x, var int y);
Declaring a parameter in the primary constructor with var or final
implicitly induces an instance variable for that parameter.
To ensure this constructor executes on every new instance, a class, mixin class, or enum with a primary constructor cannot have any other non-redirecting generative constructors.
Field declarations in parameters
#
Parameters in primary constructors with the var or final modifier,
called declaring parameters, implicitly induce a field.
If you omit the modifier, the parameter does not create a field. It behaves just like a parameter in a traditional constructor.
// Declares both fields x and y.
class Point(var int x, var int y);
// Doesn't declare a field.
class User(String name);
Because final and var modifiers on parameters are reserved exclusively
for declaring parameters in primary constructors,
you cannot use them on parameters in other kinds of functions.
For extension types, the primary constructor must have exactly one parameter.
This parameter is always a declaring parameter, even if you omit the modifier.
You can use the final modifier, but it is an error to use var.
Mixin classes can only have a primary constructor with no parameters, body, or initializer list.
Primary initializer scope
#When you use a primary constructor, the parameters you declare in the class header become directly available for initializing non-late fields in the class body.
This eliminates the need for a separate initializer list when calculating values for other fields. It works just as if you were initializing variables in a traditional constructor's initializer list.
class DeltaPoint(final int x, int delta) {
// Accesses 'x' and 'delta' parameters directly!
final int y = x + delta;
}
This makes refactoring between traditional and primary constructors simpler and safer.
Add constructor bodies
#
To validate input or perform complex initialization,
you can add a body to the primary constructor inside the class definition.
This body uses the this keyword followed by a block:
class Point(var int x, var int y) {
this : assert(x >= 0 && y >= 0) {
print('Point initialized at ($x, $y)');
}
}
This block can specify an initializer list after this and/or a function body.
It also supports forms with just an initializer list followed by a semicolon
(for example, this : assert(x >= 0);),
and you can apply metadata annotations to it
(for example, @metadata this;).
Initialize private fields
#To initialize a private field using a named parameter, you can write manual assignment boilerplate in a traditional constructor:
// Variant not using a private named parameter.
class User({required String name}) {
String _name = name;
}
With primary constructors and the private named parameters feature, you can declare the private field directly in the constructor header. When you use a private name (with a leading underscore) for a named parameter, the compiler automatically makes the parameter name public for the caller by removing the underscore:
// Variant using a private named parameter.
class User({required var String _name});
In both cases, the caller uses the public name name
at the call site: User(name: 'John Doe').
Empty bodies
#
An empty body of a class, mixin class, extension, or extension type ({})
can be replaced by a semicolon (;).
While this is true in general for these declarations, it is particularly
useful when using a primary constructor to keep the entire declaration on a single line.
class Point(var int x, var int y);
Super parameters
#Super parameters work just like they do in traditional constructors:
class A(final int a);
class B(super.a) extends A;
Constraints and breaking changes
#Keep these constraints and potential errors in mind when using primary constructors:
-
Declaring parameters cannot be
lateorexternal: Thelateandexternalmodifiers are not allowed on parameters in the primary constructor header. To use these modifiers, declare the fields in the class body as usual. - Name collisions: Declaring a parameter in the primary constructor with the same name as a method or another field in the class body results in a compile-time error.
Unless stated otherwise, the documentation on this site reflects Dart 3.11.0. Page last updated on 2026-05-12. View source or report an issue.