The meta.dart
package defines an @immutable
annotation that can be used to mark classes that are immutable, meaning that all their instance variables, directly defined or inherited, have to
be final
, that is, once the instance is created, it cannot be
modified.
However, like any other annotation, the @immutable
annotation only provides information to the developer using the class and to
developer tools such as the analyzer package, and it does not enforce any constraint.
Therefore, there is nothing that prevents a developer from
- defining a non-
const
constructor of an @immutable
class (named or unnamed)
- passing non-
const
literals to such a constructor
An example is shown in the code below:
import 'package:meta/meta.dart';
@immutable
class MultiDimensionalPoint {
final List<int> coordinates;
MultiDimensionalPoint(this.coordinates); // Non-const constructor
}
void main() {
var p = MultiDimensionalPoint([1, 2, 3]);
p.coordinates[0] = 4; // This is allowed, and won't raise any error
print(p.coordinates); // Will print [4, 2, 3]
}
This scenario can lead to confusion and bugs, since a developer might not realized the class is designed with immutability in mind, and may modify
the instance after creation, as in the example above.
Even when the immutability design constraint is respected, failing to specify the const
constraint on a literal used as an argument to
a constructor of an @immutable
class will lead to subpar performance, as the Dart compiler will not create compile-time constants in a
non-constant context.
Exceptions
Notice that the const
constraint is not required when the literal is used in a const context, as the Dart compiler is
already building a compile-time constant for a larger expression including the object, and a compile-time constant can only be built of other
compile-time constants. See rule S7112 for further details about why it’s important to call const
constructors with the
const
keyword or in a const
context.
Rule S7113 helps in this regard, by enforcing the const
constraint on the constructor itself, which in turns will oblige
all constructor parameters to be const
, whether they are literals or not.
import 'package:meta/meta.dart';
@immutable
class MultiDimensionalPoint {
final List<int> coordinates;
const MultiDimensionalPoint(this.coordinates); // Const constructor
}
void main() {
const p1 = MultiDimensionalPoint([1, 2, 3]); // [1, 2, 3] will be const since it's used in a const context
var p2 = const MultiDimensionalPoint([1, 2, 3]); // [1, 2, 3] will be const since it's used in a const context
}