Skip to content
Blog

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

AspectPrimaryTraditional
Field declarationImplicitExplicit
Constructor bodyNoYes
Initializer listNoYes
RedirectingNoYes
FactoryNoYes
ComplexitySimpleFull

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.