An intro to immutability with Dart
It’s impossible to deny that immutability is a hot topic in programming, especially front-end programming. Libraries like Immutable.js and…
It’s impossible to deny that immutability is a hot topic in programming, especially front-end programming. Libraries like Immutable.js and other concepts like unidirectional data flow have argued it’s easier to reason about data when it doesn’t change underneath you:
In object-oriented and functional programming, an immutable object (unchangeable object) is an object whose state cannot be modified after it is created. This is in contrast to a mutable object (changeable object), which can be modified after it is created.
Well, what about Dart? We have a couple concepts that lend themselves well to immutability and immutable objects built-in, starting with the const modifier and const constructor. Not to be confused with const in ES6, which is merely an immutable binding:
// Define USER as a constant and give it an initial value.
const USER = { name: 'Joe'; }
// This will throw an error.
USER = {};
// But this will not.
USER.name = 'Jill';
In Dart, const is both an immutable binding and an immutable object:
main() {
const user = const {'name': 'Joe'};
// Static error: "Constant variables cannot be assigned a value".
user = {};
// Runtime error: "Unsupported operation: Cannot modify unmodifiable Map".
user['name'] = 'Jill';
}
All literals (Null, String, int, double, num, bool, Map, List, Symbol) are capable of being const, and it’s possible to create user-types with a const constructor:
class User {
final String name;
const User(this.name);
}
main() {
const user = const User('Joe');
}
Let’s review — const instances are both immutable bindings and enforced at a language level to be both deeply immutable — and also are canonicalized at compile-time — that is, any two instances are considered equivalent, and are only represented by a single instance when running. For example, the following is rather cheap — it only allocates a single instance at runtime:
class User {
final String name;
final List<String> cars;
User(this.name, {this.cars});
}
main() {
for (var i = 0; i < 100; i++) {
const users = const {
'Matan': const User(
'Matan Lurey',
cars: const [
'Truck',
'Jeep',
'GoKart',
],
),
};
}
}
Want to learn more? Read the Dart language tour about final and const.
Further static checking with package:meta
#Of course, const is a bit restrictive — you must be able to create a class at compile-time — so you couldn’t for example read a database and create const objects at runtime. We’ve recently introduced the “immutable” annotation with package:meta:
import 'pacakge:meta/meta.dart';
// Error: This class inherits from a class marked as @immutable, and therefore
// should be immutable (all instance fields must be final).
@immutable
class User {
String name;
}
You can use this annotation to help enforce that developers keep your classes deeply immutable. It won’t be canonicalized like const, but can still be quite helpful for developers.
I hope this was a nice introduction to immutability. Please let me know in the comments or on twitter if you’d like to learn more about Dart or immutability.
More from Dart
Dart in Google Summer of Code 2026
We’re excited to announce that the Dart project will mentor contributors for the seventh time in Google Summer of Code 2026!
Google Summer of Code 2025 Results
The Google Summer of Code (GSoC) program focuses on engaging new developers worldwide with open source software development. Google…