Built Values for Dart - Introduction
Built Value provides:
- Immutable value types;
- EnumClass, classes that behave like enums;
- JSON serialization.
Immutable collections are from built_collection.
See the API docs.
Articles
built_value
for Immutable Object Modelsbuilt_value
for Serialization- Building a Chat App in Dart
- End to End Testing in One Short Second with Dart
- Moving Fast with Dart Immutable Values
- Flutter JSON Serialization
- Flutter TODO App Example
using
built_value
, built_redux, and flutter_built_redux - Building a (large) Flutter app with Redux
- Some Options for Deserializing JSON with Flutter
Tutorials
Tools
- Json to Dart built_value class converter
- Json or js Object to Dart built_value class converter
- VSCode extension
Examples
For an end to end example see the chat example, which was demoed at the Dart Summit 2016. The data model, used both client and server side, uses value types, enums and serialization from built_value.
Simple examples are here.
Since v5.2.0
codegen is triggered by running pub run build_runner build
to
do a one-off build or pub run build_runner watch
to continuously watch your
source and update the generated output when it changes. Note that you need a
dev dependency on built_value_generator
and build_runner
. See the example
pubspec.yaml.
If using Flutter, the equivalent command is flutter packages pub run build_runner build
.
Alternatively, put your built_value
classes in a separate Dart package with no dependency
on Flutter. You can then use built_value
as normal.
If using a version before v5.2.0, codegen is triggered via either a build.dart to do a one-off build or a watch.dart to continuously watch your source and update generated output.
Value Types
Value types are, for our purposes, classes that are considered interchangeable if their fields have the same values.
Common examples include Date
, Money
and Url
. Most code introduces
its own value types. For example, every web app probably has some
version of Account
and User
.
Value types are very commonly sent by RPC and/or stored for later retrieval.
The problems that led to the creation of the Built Value library have been discussed at great length in the context of AutoValue for Java.
In short: creating and maintaining value types by hand requires a lot of boilerplate. It’s boring to write, and if you make a mistake, you very likely create a bug that’s hard to track down.
Any solution for value types needs to allow them to participate in object
oriented design. Date
, for example, is the right place for code that
does simple date manipulation.
AutoValue solves the problem for Java with code generation, and Built Values does the same for Dart. The boilerplate is generated for you, leaving you to specify which fields you need and to add code for the behaviour of the class.
Enum Class
Enum Classes provide classes with enum features.
Enums are very helpful in modelling the real world: whenever there are a small fixed set of options, an enum is a natural choice. For an object oriented design, though, enums need to be classes. Dart falls short here, so Enum Classes provide what’s missing!
Design:
- Constants have
name
andtoString
, can be used inswitch
statements, and are real classes that can hold code and implement interfaces - Generated
values
method that returns all the enum values in aBuiltSet
(immutable set) - Generated
valueOf
method that takes aString
FAQ
How do I check a field is valid on instantiation?
The value class private constructor runs when all fields are initialized and can do arbitrary checks:
abstract class MyValue {
MyValue._() {
if (field < 0) {
throw ArgumentError(field, 'field', 'Must not be negative.');
}
}
How do I process a field on instantiation?
Add a hook that runs immediately before a builder is built. For example, you could sort a list, so it’s always sorted directly before the value is created:
abstract class MyValue {
@BuiltValueHook(finalizeBuilder: true)
static void _sortItems(MyValueBuilder b) =>
b..items.sort();
How do I set a default for a field?
Add a hook that runs whenever a builder is created:
abstract class MyValue {
@BuiltValueHook(initializeBuilder: true)
static void _setDefaults(MyValueBuilder b) =>
b
..name = 'defaultName'
..count = 0;
Should I check in and/or publish in the generated .g.dart
files?
See the build_runner docs. You usually should not check in generated files, but you do need to publish them.
Features and bugs
Please file feature requests and bugs at the issue tracker.