prefer_primary_constructors
v1.0.0 Warning Fix Code Quality
Warns when a class or enum declares a generative constructor that can move into the type header as a Dart 3.13 primary constructor. The field list, the parameter list and the assignments stop being three copies of the same information.
A type that declares no constructor is never reported. That is the difference from the SDK’s use_primary_constructors, which also wants enum Sport() { and abstract interface class Gateway() {.
Why use this rule
Section titled “Why use this rule”A class written the old way names each field three times: in the field declaration, in the constructor parameter list, and in the assignment. A primary constructor declares all three at once, so adding a field and forgetting the constructor stops being possible.
See also: Primary constructors | Feature specification
// LINT: the fields, the parameters and the assignments are three copies// of the same list.class CartLine { final String sku; final int quantity; CartLine(this.sku, this.quantity);
int get total => quantity * 2;}// LINT: an enum constructor moves too.enum Currency { pln('zł'), eur('€');
const Currency(this.symbol);
final String symbol;}class CartLine(final String sku, final int quantity) { int get total => quantity * 2;}enum Currency(final String symbol) { pln('zł'), eur('€');}Initializer lists and bodies move into a this block
Section titled “Initializer lists and bodies move into a this block”class Account(final String id, int balance) { final int cents;
this : cents = balance * 100, assert(balance >= 0);}Types without a constructor stay as they are
Section titled “Types without a constructor stay as they are”enum Sport { squash, padel }
abstract interface class Gateway { Future<void> send();}What is reported
Section titled “What is reported”A class or enum with exactly one generative constructor that does not redirect. Any other constructor must be a factory or redirect with : this(...), since that is all a primary constructor allows beside it. The class may have methods, other fields, a superclass, super.x parameters, plain parameters, an initializer list and a body.
Not reported, because the rewrite has no faithful form:
- a second generative constructor that does not redirect,
- an annotation on the constructor,
- a
this.xwhose field islate,covariant,externalorabstract, int this.x(an explicit type may be narrower than the field’s) andthis.cb(int v),final int a, b;when the constructor declares only some of them,- a
mixin classwhose constructor is not trivial.
The library must be on language version 3.13 or later. A file pinned to an older version is skipped.
Quick fix
Section titled “Quick fix”The fix rewrites the header and the body in one edit:
- The parameter list is the constructor’s own, with each
this.xreplaced byfinal int x(orvar int xfor a mutable field). Order,required, defaults and brace groups stay as they were. constmoves onto the header (class const Point(...)). An enum’s constructor is implicitly const, so the fix leaves it out.- An initializer list and a non-empty body go into a
thisblock. An initializer list is never turned into a field initializer. - Comments are never deleted. A field’s comments, doc comment and annotations move with it into the header. The constructor’s comments move there too, unless a
thisblock remains for them to stay above. - A class body left empty becomes
;.
The quick fix works in the IDE. dart fix --apply in Dart 3.13 does not apply fixes from analyzer plugins.
Interaction with SDK lints
Section titled “Interaction with SDK lints”Turn off the SDK’s use_primary_constructors when you use this rule. It reports the same constructors, plus every type that has none.
use_declaring_parameters, unnecessary_primary_constructor_body and empty_container_bodies only look at code that already migrated, so they combine well with this rule. unnecessary_type_name_in_constructor suggests new(this.x) for the same constructor. Either fix leaves code this rule still converts.
Turning this rule off
Section titled “Turning this rule off”This rule is in the opinionated preset, so it is on with
preset: opinionated, or by name:
rules: prefer_primary_constructors: trueTo turn it off again:
rules: prefer_primary_constructors: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_accessing_other_classes_private_members— Make the underscore mean what everyone reads it as.avoid_commented_out_code— Detect and flag commented-out code.avoid_complex_conditions— Keep boolean conditions within an operand budget.avoid_deep_nesting— Keep control flow within a nesting budget.