← → or space · progress saves for Continue on the roadmap
Goal
Use final instance fields so object identity stays tied to stable field values unless you replace the whole object.
Step 1 - final field
class Point {
final double x;
final double y;
const Point(this.x, this.y);
}
void main() {
Point p = const Point(1, 2);
print('${p.x}, ${p.y}');
}finalmeans each instance sets the field once (in the constructor or initializer), then it cannot be reassigned.
Step 2 - Not immutable if internals are mutable
class Bag {
final List<String> items;
Bag(this.items);
}
void main() {
Bag b = Bag(['a']);
b.items.add('b');
print(b.items);
}- The
Bagreference does not replaceitems, but the list inside still mutates. True immutability often meansList.unmodifiableor defensive copies (covered more in builds).
Step 3 - Immutable class recipe (typical)
- All fields
final(or getters over frozen data). - No methods that mutate internal collections in place; return copies or new instances instead.
constconstructor when every field can beconst(optional but nice for literals).
Good habit
- Say “immutable at the field level” vs “deeply immutable”: they are different.
Practice tasks
- Change
Pointso it is notconstbut still usesfinalfields with a normal constructor. - Make a
Labelclass withfinal String textonly; try to assigntextlater and fix the error by using a newLabelinstead.