prefer-const-constructors-in-immutables

Same as prefer_const_constructors_in_immutables in Dart lints

Group
Style
Path
lint/style/prefer-const-constructors-in-immutables
Default
Off, opt in
Origin
Dart lints
Domain
flutter
Rule
328 of 512
flutter

Prefers const constructors in immutable classes and extension types.

Classes and mixin classes are checked only when they have no with clause; mixin declarations and classes applying mixins are skipped. The declaration must be annotated @immutable or inherit it through extends only, not through implements or applied mixins. Extension types are also checked when annotated @immutable.

A non-const, non-primary constructor whose body is ; is reported when:

  • it is a redirecting factory to a const constructor; or
  • it is generative, delegates to a const constructor, has no non-final instance fields on its superclass/mixin chain, and every field initializer, initializer-list expression, assert, and super/this argument is potentially constant.

Delegation follows an explicit super or this call, otherwise the superclass's unnamed constructor (or an extension type's representation constructor). Implicit default constructors are not const. Ordinary constructor diagnostics cover their first token after metadata.

Analyzer 3.13 class and mixin-class primary constructors use the same body, delegation and field-finality checks, but do not reject nonconstant initializer expressions. Immutable extension-type primary constructors need only lack const and a block or expression body; even an empty block disqualifies them, but delegation and field finality are not checked.

Invalid

example.dartdart
// @dart = 3.13

import 'package:meta/meta.dart';
class ConstValue {
  const ConstValue.named();
}

@immutable
class ConstFieldInitializer {
  final ConstValue value = const ConstValue.named();
  ConstFieldInitializer();
}

@immutable
class LaxOperands {
  final Object value;
  LaxOperands(Object a, Object b, List<int> l) : value = (a == b, '$a', l.length, a is int, int);
  external LaxOperands.external();
}

@immutable
class Value {
  final int value;
  Value(this.value);
}

@immutable
abstract class ImmutableBase {
  const ImmutableBase.named();
}
class Inherited extends ImmutableBase {
  final int value;
  Inherited(this.value) : super.named();
}

@immutable
class Redirecting {
  final int value;
  const Redirecting.target(this.value);
  Redirecting(int value) : this.target(value);
}

@immutable
abstract class Interface {
  factory Interface() = Implementation;
}
class Implementation implements Interface {
  const Implementation();
}

@immutable
class Primary() {}

@immutable
class PrimaryField(final int value) {}

@immutable
class NamedPrimary<T>.named(final T value) {}

@immutable
abstract class WidgetBase {
  const WidgetBase({Object? key});
}

abstract class PrimaryWidget({super.key}) extends WidgetBase {}

abstract class PrimaryWidgetWithoutKey() extends WidgetBase {}

abstract class PrimaryWidgetBody() extends WidgetBase {
  @deprecated
  this;
}

@immutable
class PrimaryInitializer(int value) {
  final int field;
  this : field = value, assert(value >= 0);
}

class PrimaryNamedSuper(final int value) extends ImmutableBase {
  this : super.named();
}

@immutable
extension type Representation(List<int> value) {}

@immutable
extension type NamedRepresentation<T>.named(T value) {}

@immutable
class PrimaryRuntimeInitializer() {
  final DateTime value;
  this : value = DateTime.now();
}

@immutable
class PrimaryRuntimeFieldInitializer() {
  final DateTime value = DateTime.now();
}

@immutable
mixin class PrimaryMixin() {}

@immutable
extension type const ConstPrimaryOrdinary(int value) {
  ConstPrimaryOrdinary.other(this.value);
}

@immutable
extension type NonConstPrimaryOrdinary(int value) {
  NonConstPrimaryOrdinary.other(this.value);
}

@immutable
extension type EmptyRepresentationBody(int value) {
  this;
}

@immutable
class TearoffInitializer {
  final Object callback;
  TearoffInitializer() : callback = TearoffInitializer.target;
  const TearoffInitializer.target() : callback = 0;
  factory TearoffInitializer.redirect() = TearoffInitializer.target;
}

@Immutable()
class AnnotationConstructor {
  AnnotationConstructor();
}

@immutable
extension type const ConstRepresentation(int value) {
  ConstRepresentation.named(this.value);
}

@immutable
extension type RuntimeRepresentation(int value) {}
Declare a `const` constructor in an immutable class.
10 final ConstValue value = const ConstValue.named();
11 ConstFieldInitializer();
Declare a `const` constructor in an immutable class.
16 final Object value;
17 LaxOperands(Object a, Object b, List<int> l) : value = (a == b, '$a', l.length, a is int, int);
Declare a `const` constructor in an immutable class.
17 LaxOperands(Object a, Object b, List<int> l) : value = (a == b, '$a', l.length, a is int, int);
18 external LaxOperands.external();
Declare a `const` constructor in an immutable class.
23 final int value;
24 Value(this.value);
Declare a `const` constructor in an immutable class.
32 final int value;
33 Inherited(this.value) : super.named();
Declare a `const` constructor in an immutable class.
39 const Redirecting.target(this.value);
40 Redirecting(int value) : this.target(value);
Declare a `const` constructor in an immutable class.
44abstract class Interface {
45 factory Interface() = Implementation;
Declare a `const` constructor in an immutable class.
51@immutable
52class Primary() {}
Declare a `const` constructor in an immutable class.
54@immutable
55class PrimaryField(final int value) {}
Declare a `const` constructor in an immutable class.
57@immutable
58class NamedPrimary<T>.named(final T value) {}
Declare a `const` constructor in an immutable class.
64
65abstract class PrimaryWidget({super.key}) extends WidgetBase {}
Declare a `const` constructor in an immutable class.
66
67abstract class PrimaryWidgetWithoutKey() extends WidgetBase {}
Declare a `const` constructor in an immutable class.
68
69abstract class PrimaryWidgetBody() extends WidgetBase {
Declare a `const` constructor in an immutable class.
74@immutable
75class PrimaryInitializer(int value) {
Declare a `const` constructor in an immutable class.
79
80class PrimaryNamedSuper(final int value) extends ImmutableBase {
Declare a `const` constructor in an immutable class.
84@immutable
85extension type Representation(List<int> value) {}
Declare a `const` constructor in an immutable class.
87@immutable
88extension type NamedRepresentation<T>.named(T value) {}
Declare a `const` constructor in an immutable class.
90@immutable
91class PrimaryRuntimeInitializer() {
Declare a `const` constructor in an immutable class.
96@immutable
97class PrimaryRuntimeFieldInitializer() {
Declare a `const` constructor in an immutable class.
101@immutable
102mixin class PrimaryMixin() {}
Declare a `const` constructor in an immutable class.
105extension type const ConstPrimaryOrdinary(int value) {
106 ConstPrimaryOrdinary.other(this.value);
Declare a `const` constructor in an immutable class.
109@immutable
110extension type NonConstPrimaryOrdinary(int value) {
Declare a `const` constructor in an immutable class.
114@immutable
115extension type EmptyRepresentationBody(int value) {
Declare a `const` constructor in an immutable class.
121 final Object callback;
122 TearoffInitializer() : callback = TearoffInitializer.target;
Declare a `const` constructor in an immutable class.
123 const TearoffInitializer.target() : callback = 0;
124 factory TearoffInitializer.redirect() = TearoffInitializer.target;
Declare a `const` constructor in an immutable class.
128class AnnotationConstructor {
129 AnnotationConstructor();
Declare a `const` constructor in an immutable class.
133extension type const ConstRepresentation(int value) {
134 ConstRepresentation.named(this.value);
Declare a `const` constructor in an immutable class.
137@immutable
138extension type RuntimeRepresentation(int value) {}

Valid

example.dartdart
// @dart = 3.13

import 'package:meta/meta.dart' as meta;
@meta.immutable
class Value {
  final int value;
  const Value(this.value);
}

@meta.immutable
abstract class NonConstBase {
  final DateTime created;
  NonConstBase() : created = DateTime.now();
}
class CannotInheritConst extends NonConstBase {
  final int value;
  CannotInheritConst(this.value);
}

@meta.immutable
class RuntimeInitializer {
  final DateTime value;
  RuntimeInitializer() : value = DateTime.now();
}

@meta.immutable
class RuntimeFieldInitializer {
  final DateTime value = DateTime.now();
  RuntimeFieldInitializer();
}

@meta.immutable
class ShadowedConstConstructor {
  final Value value;
  ShadowedConstConstructor(Value Function() Value) : value = Value();
}

@meta.immutable
class GenericCalleeShadow<ConstValue> {
  final Object value;
  GenericCalleeShadow() : value = ConstValue.named();
}

@meta.immutable
class MemberCalleeShadow {
  static final ConstValue = () => const Value(0);
  final Object value;
  MemberCalleeShadow() : value = ConstValue();
}

@meta.immutable
class FactoryBody {
  const FactoryBody._();
  factory FactoryBody() => const FactoryBody._();
}

class ConstValue {
  const ConstValue.named();
}

@meta.immutable
class CallShapedFieldInitializer {
  final ConstValue value = ConstValue.named();
  CallShapedFieldInitializer();
}

@meta.immutable
abstract class ImplicitConstructorBase {}
class ImplicitSuper extends ImplicitConstructorBase {
  final int value;
  ImplicitSuper(this.value);
}

@meta.immutable
class BlockBody {
  final int value;
  const BlockBody.named(this.value);
  BlockBody(this.value) {}
}

@meta.immutable
class NonLengthProperty {
  final bool value;
  const NonLengthProperty.named(this.value);
  NonLengthProperty(int x) : value = x.isEven;
  NonLengthProperty.list(int x) : value = [x].isEmpty;
  NonLengthProperty.bang(bool? x) : value = x!;
}

@meta.immutable
abstract class Marker {}
class InterfaceOnly implements Marker {
  final int value;
  InterfaceOnly(this.value);
}

@meta.immutable
class const ConstPrimary(final int value) {}

class UnannotatedPrimary(final int value) {}

@meta.immutable
class MutablePrimary(var int value) {}

@meta.immutable
class MutableBodyField() {
  int value = 0;
}

@meta.immutable
class PrimaryBlock() {
  this {}
}

@meta.immutable
abstract class AbstractPrimaryBlock() {
  this {}
}

class PrimaryNonConstSuper(final int value) extends NonConstBase {}

class PrimaryImplicitSuper(final int value) extends ImplicitConstructorBase {}

class PrimaryInterfaceOnly(final int value) implements Marker {}

mixin EmptyMixin {}

@meta.immutable
class PrimaryWithMixin() with EmptyMixin {}

@meta.immutable
mixin class ImmutableMixin {}

class PrimaryMixinAnnotationOnly() with ImmutableMixin {}

@meta.immutable
extension type const ConstRepresentation(List<int> value) {}

extension type UnannotatedRepresentation(List<int> value) {}

@meta.immutable
extension type const NamedConstRepresentation<T>.named(T value) {}

@meta.immutable
mixin class const ConstPrimaryMixin() {}

mixin class UnannotatedPrimaryMixin() {}

@meta.immutable
extension type const ConstOrdinaryRepresentation(int value) {
  const ConstOrdinaryRepresentation.other(this.value);
  ConstOrdinaryRepresentation.block(this.value) {}
}

@meta.immutable
extension type BlockRepresentationBody(int value) {
  this {}
  BlockRepresentationBody.other(this.value);
}

@meta.immutable
class NonConstCollectionField {
  final List<int> values = [];
  final List<int> nonEmpty = [1];
  final Set<int> emptySet = <int>{};
  NonConstCollectionField();
}

@meta.immutable
class NonConstCollectionInitializers {
  final Object value;
  NonConstCollectionInitializers.list() : value = [1];
  NonConstCollectionInitializers.map() : value = <int, int>{};
  NonConstCollectionInitializers.set() : value = <int>{};
}

Configure prefer-const-constructors-in-immutables

Set its severity in falcon.json. See the configuration guide.

falcon.jsonjson
{
  "linter": {
    "rules": {
      "style": {
        "prefer-const-constructors-in-immutables": "error"
      }
    }
  }
}

Or silence one occurrence with a suppression comment; the reason is required.

example.dartdart
// falcon-ignore lint/style/prefer-const-constructors-in-immutables: <reason>