Primary Constructors in Dart 3.13: Cleaner Class Definitions
Dart 3.13 introduces primary constructors that reduce boilerplate by declaring parameters directly in the class header. Learn syntax, use cases, and migration tips.
Published on • September 15, 2026
AI Assistant

Dart 3.13 introduces primary constructors, a syntax that eliminates boilerplate for simple classes. Instead of declaring fields and a constructor body separately, you declare parameters directly in the class header.
Before primary constructors
class User {
final String name;
final String email;
final int age;
User({
required this.name,
required this.email,
required this.age,
});
@override
String toString() => 'User($name, $email, $age)';
}
Four lines of field declarations, three lines of constructor — for a simple data class.
After primary constructors
class User(String name, String email, int age) {
@override
String toString() => 'User($name, $email, $age)';
}
The parameters are declared directly in the class header. They’re available as fields throughout the class body.
Syntax rules
Primary constructors follow specific rules:
// Parameters without this. are not fields
class Foo(int x) {
int get doubled => x * 2; // x is accessible
}
// Parameters with this. create fields
class Bar(this.name, this.age);
// Required named parameters
class Baz({required String name, required int age});
// Optional named parameters
class Qux({String name = 'unknown', int age = 0});
// Mix of positional and named
class Complex(String id, {required String name, int age = 0});
Primary constructors vs. traditional constructors
| Aspect | Primary | Traditional |
|---|---|---|
| Field declaration | Implicit | Explicit |
| Constructor body | No | Yes |
| Initializer list | No | Yes |
| Redirecting | No | Yes |
| Factory | No | Yes |
| Complexity | Simple | Full |
Use primary constructors for simple data classes. Use traditional constructors for complex initialization logic.
Initializer lists are not supported
Primary constructors don’t support initializer lists:
// Not possible with primary constructor
class User(String name, int age) {
final int birthYear; // Cannot initialize from parameter
}
// Use traditional constructor for this
class User {
final String name;
final int birthYear;
User(this.name, int age) : birthYear = DateTime.now().year - age;
}
Bodies are not supported
Primary constructors can’t have bodies:
// Not possible
class User(String name) {
print('Creating user: $name'); // Error
}
// Use traditional constructor
class User {
final String name;
User(this.name) {
print('Creating user: $name');
}
}
Migration strategy
Migrate gradually — primary constructors are opt-in:
// Start with simple data classes
class Point(double x, double y);
class Color(int r, int g, int b, int a);
// Then move to classes with methods
class Rectangle(double x, double y, double width, double height) {
double get area => width * height;
bool contains(Point point) =>
point.x >= x && point.x <= x + width &&
point.y >= y && point.y <= y + height;
}
IDE refactoring
IDEs support automatic conversion:
// VS Code: Convert to primary constructor
// 1. Place cursor on class
// 2. Ctrl+Shift+P → "Convert to primary constructor"
// 3. Review and confirm
Android Studio and IntelliJ provide the same refactoring.
Use cases
Data transfer objects (DTOs):
class ApiResponse(String status, List<String> data, String? error);
Configuration objects:
class DatabaseConfig(String host, int port, String name, {bool ssl = true});
Value objects:
class Money(double amount, String currency);
class Email(String value);
Lint rules
Dart 3.13 adds new lint rules:
# analysis_options.yaml
linter:
rules:
- use_declaring_parameters
- unnecessary_primary_constructor_body
- unnecessary_type_name_in_constructor
These rules suggest migrating to primary constructors where appropriate.
Constant primary constructors
Primary constructors can be const:
class Point(double x, double y);
const origin = Point(0, 0);
const unit = Point(1, 1);
Named primary constructors
Named primary constructors are not supported. Use traditional constructors for named constructors:
class Color(int r, int g, int b) {
// Named constructors still use traditional syntax
Color.named(this.r, this.g, this.b);
Color.fromHex(String hex);
}
Primary constructors reduce noise in Dart code. They’re not revolutionary, but they eliminate the tedious boilerplate that every Dart developer has written thousands of times.