Immutable collections and objects for Flutter and Dart
Immutability is a known good principle of software engineering. Whatever doesn't need to change after it's created, it shouldn't. You get a lot of goodies with this approach, among others, fewer bugs because something is changed accidentally and thread-safety for "free".
So it's kind of sad that Dart (and by extension Flutter) does not have built-in support for immutable objects and collections.
That's not a big issue though because built_collection and built_value packages exist. With 2 additional lines in your dependency list, you get immutable collections and immutable objects respectively.
I will provide a super concise overview of the usage of both packages. Check out the respective documentation for additional examples and features.
Set up
Add these to your pubspec.yaml
(check built_collection and built_value for the latest versions).
dependencies:
built_collection: ^4.3.2
built_value: ^7.1.0
Immutable collections
Offering BuiltList
, BuiltSet
, BuiltMap
which are the SDK equivalents but once they are created cannot change (mutate). Use the convenient extension methods to create them from a mutable equivalent.
import 'package:built_collection/built_collection.dart';
final builtList = [1, 2, 3].build();
final builtSet = {1, 2, 3}.build();
final builtMap = {1: 'one', 2: 'two', 3: 'three'}.build();
To modify an immutable collection, call toBuilder()
to get a mutable version. When you are done with your changes, call build()
again to make it immutable.
import 'package:built_collection/built_collection.dart';
final listBuilder = builtList.toBuilder();
listBuilder.addAll([4, 5, 6]);
final newBuiltList = listBuilder.build();
Note that build_collection
does not implement the mutable equivalent interfaces. For instance, a method accepting List<Int>
won't be able to accept a BuiltList<Int>
. In case you want to accept both, the common ancestor is Iterable<Int>
.
Another goodie of these collections is that they are comparable and hashable out-of-the-box (in contrast to their mutable counterparts). Finally, null
is not allowed in any of these collections.
Immutable objects
These are for creating data value classes (similar to Kotlin's data classes). It's using code generation, so you would need to include the generated file in your imports using the part
statement. The boilerplate is a bit verbose, but it gets the job done.
For more complex / bigger objects, you can just provide the builder interface directly. Note that by default, setting a value to null is not allowed except if explicitly annotating the field appropriately. Finally, you can convert an immutable built_value to a mutable form by calling toBuilder()
.
By now, you have a grasp of how to use immutable collections and objects in your Flutter apps. Bear in mind that this only scratches the surface of these packages (e.g. buit_value offers serializers/deserializers). Check out their respective docs for more.
Happy coding!