In Dart, what must be true of a class for it to have a const constructor, and does calling it always produce a constant?
answer
- every field final
- no body block
- potentially constant initializers
- const super constructor
- const keyword or constant context
basics
~20 sA const constructor needs every instance field final, no body, constant-evaluable initializers and a const superclass constructor. It yields a compile-time constant only when invoked with const or in a constant context; otherwise it creates an ordinary new object.
solid answer
~40 sA `const` constructor lets instances be built at compile time. The class must be deeply suitable for that: every instance field `final` (and none `late`), no constructor body, every initializer and initializer-list expression potentially constant — built only from constants and the constructor's parameters — and any superclass constructor it calls must itself be `const`. Asserts in the initializer list are allowed. Declaring the constructor `const` does not make every call constant: `const Money(0, 'EUR')` or a use inside a constant context creates a constant, while `Money(0, 'EUR')` in ordinary code allocates a normal instance. In Flutter this is why lints ask for `const` at widget call sites — the constructor makes it possible, the call site decides.
code
dart · 12 linesvoid main() {
const a = Money(0, 'EUR');
final b = Money(0, 'EUR');
const c = Money(0, 'EUR');
print(identical(a, c)); // true: both are the same constant
print(identical(a, b)); // false: b was created at runtime
final cents = DateTime.now().second;
final d = Money(cents, 'EUR'); // fine; `const Money(cents, 'EUR')` would not compile
print(d.cents >= 0); // true
}go deeper
Recall that a const constructor needs final fields and no body, and that const at the call site creates the constant.
Explain the full requirement list, potentially constant initializers and const supers, and why Money(0, 'EUR') without const is an ordinary allocation.
Decide deliberately whether a public value type is const-capable, knowing that adding const is easy and removing it breaks callers.
Weigh const-compatibility of shared types against future flexibility such as caching, lazy fields or runtime validation.
## What a const constructor is for A **constant** in Dart is a value computed at compile time. A class can take part by declaring a **const constructor**, which allows expressions like `const Money(0, 'EUR')` to be evaluated by the compiler rather than at runtime. ```dart class Money { const Money(this.cents, this.currency) : assert(cents >= 0), assert(currency.length == 3); final int cents; final String currency; static const zeroEuro = Money(0, 'EUR'); } ``` ## Requirements the analyzer enforces 1. **All instance fields are `final`.** A single mutable field is `const_constructor_with_non_final_field`. 2. **No `late` fields.** A `late final` field conflicts with a generative const constructor (`late_final_field_with_const_constructor`). 3. **No body.** Even an empty block is `const_constructor_with_body`; a const constructor ends with `;` after its initializer list. 4. **Constant-evaluable initializers.** Field initializers at the declaration must be constants, and initializer-list expressions must be *potentially constant* — built from constants, the constructor's parameters and the operations allowed in constant expressions (`const_constructor_with_field_initialized_by_non_const`). 5. **A const super constructor.** If the class extends another, the superclass constructor it calls must be `const` (`const_constructor_with_non_const_super`); a mixin that adds a field blocks it too. 6. **Redirects stay const.** A const redirecting constructor must target a const constructor. `assert`s are permitted in the initializer list and are checked when the constant is evaluated. ## `const` at the call site decides This is the part candidates miss. The constructor makes constant creation **possible**; the call site **chooses** it: | Call | Result | |---|---| | `const Money(0, 'EUR')` | compile-time constant | | `static const zero = Money(0, 'EUR');` | constant — the declaration is a constant context | | `const [Money(1, 'EUR')]` | constant — inside a constant context `const` is implied | | `Money(0, 'EUR')` in ordinary code | a new, non-constant instance | | `Money(cents, 'EUR')` with a runtime `cents` | non-constant; `const` here would not compile | So `var a = Money(0, 'EUR');` allocates a fresh object every time, exactly like a non-const constructor. Two constant expressions with equal arguments evaluate to the same canonical instance, a rule covered with constant variables. ## Consequences for design - A const constructor is a **public promise**. Removing `const` later breaks every caller that wrote `const Money(...)` — Effective Dart warns about exactly this. - `const` forbids runtime work: no caching maps, no normalisation with instance methods, no I/O. Validation is limited to `assert`s over constant-evaluable expressions. - An abstract class can offer a const constructor that yields a concrete type through a **const redirecting factory**, `const factory Shape.unit() = _UnitSquare;`. ## Why Flutter developers meet this daily Flutter widgets are immutable configuration objects, so most widget classes declare `const` constructors — `const Text('Hi')`, `const SizedBox(height: 8)`. The analyzer's `prefer_const_constructors` lint then asks call sites to add `const` where every argument is constant, and `prefer_const_constructors_in_immutables` asks `@immutable` classes to declare their constructors `const`. Whether that saves rebuild work is a performance topic of its own; the language rule is the one above: the class qualifies, and the call site opts in.
- In Dart, why is removing const from a published constructor a breaking change?Callers may have written `const Money(...)` or used the constructor inside constant contexts such as `static const` fields, default parameter values or `const` lists. Once the constructor is no longer `const`, all of those stop compiling. Effective Dart therefore treats `const` on a public constructor as a commitment to keep the class constant-compatible.
- In Dart, can a const constructor validate its arguments?Only with `assert`s in the initializer list, whose conditions must be potentially constant expressions over the parameters, such as `assert(cents >= 0)`. There is no body, so no statements, loops or throws. For richer validation, keep a const constructor for known-good values and add a factory or static method that validates runtime input.
saying these in an interview costs you the question
- Declaring a const constructor makes every call produce a constant
- A const constructor may have an empty body block
- A class can have a const constructor while one field is mutable
- const constructors may run arbitrary code as long as it has no side effects
- A const constructor can call any superclass constructor