built_value: immutable value types and generated serialisation for Dart
Immutable value types, enum classes, and serialization.
At a glance
- What is it?
- built_value is a Dart library that gives you immutable value types, enum classes that behave like enums, and JSON serialisation, with the boilerplate produced by a code generator. It is the Dart answer to the problem AutoValue solves for Java, and it comes from Google with a decade of published releases behind it.
- Who is it for?
- Adopt built_value if your Dart or Flutter project has domain objects that are compared by value, sent over RPC or persisted, and you would rather generate the equality, the builder and the serialiser than write them. Do not adopt it for simple data classes that never leave a widget, because the generator and the build step cost more than the boilerplate you would have written.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly Dart, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: value types in Dart, and the equality you did not write
A value type is a class that is considered interchangeable with another if its fields have the same values. The documentation gives the common examples as Date, Money and Url, and then makes the point that most code introduces its own value types, so every web app probably has some version of Account and User.
These objects are also very commonly sent by RPC or stored for later retrieval, which is where the problem bites. The problems that led to the creation of the library were discussed at length in the context of AutoValue for Java, and the summary in the documentation is direct: creating and maintaining value types by hand requires a lot of boilerplate, it is boring to write, and if you make a mistake you very likely create a bug that is hard to track down.
That last clause is the important one. The bug in a hand-written value class is rarely a crash, it is a missing field in the equality check, so two logically identical objects compare unequal, a set holds both, a diff shows a change that did not happen, and the symptom appears far from the cause.
The constraint on the solution is also stated: any solution for value types needs to allow them to participate in object oriented design, and Date is given as the right place for code that does simple date manipulation. In other words, the goal is not a record type bolted onto the language, it is a class that behaves like a first class citizen while also comparing by value.
The licence is BSD-3-Clause, the language is Dart, the package is on pub.dev as built_value, and the last push was on 2026-09-02, with v8.13.0 published the same day.
The mechanism: you write the class, the generator writes the rest
AutoValue solves the problem for Java with code generation, and the documentation says 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.
That is the whole design, and it is why the library is a package rather than a base class you extend. The equality, the hash code, the copy and the builder are derived from the field list, so they cannot drift out of sync with the fields the way a hand-written implementation does.
The bit of boilerplate that remains is the connection to the generated code, and the documentation is practical about it: value types require a bit of boilerplate in order to connect it to generated code, and even this bit can be automated using code snippet support in your favourite text editor. The example given is an IntelliJ live template:
abstract class $CLASS_NAME$ implements Built<$CLASS_NAME$, $CLASS_NAME$Builder> {
$CLASS_NAME$._();
factory $CLASS_NAME$([void Function($CLASS_NAME$Builder) updates]) = _$$$CLASS_NAME$;
}With that template you only have to manually enter a name for your data class, which the documentation notes is something that cannot be automated. That is an honest division of labour: the generator cannot guess a name, but it can and does generate everything else.
The corresponding editor support also exists outside the template, with a VSCode extension and an IntelliJ plugin for built_value snippets, so the same template can be applied without remembering its text.
EnumClass, for the cases Dart's own enums cannot cover
Enums are useful for modelling the real world whenever there is a small fixed set of options, and for an object oriented design they need to be classes. The documentation says plainly that Dart falls short here, and Enum Classes provide what is missing.
The design has three parts. Constants have name and toString, can be used in switch statements, and are real classes that can hold code and implement interfaces. There is a generated values method that returns all the enum values in a BuiltSet, which is an immutable set. And there is a generated valueOf method that takes a String.
Each of those closes a specific gap. Holding code and implementing interfaces is what turns an enum into a class, and the values method being backed by an immutable set means the collection of enum values cannot be mutated by a caller who has a reference to it. The valueOf method is the other direction, turning a string back into a constant, which is what you need when the value arrives from a server or from storage.
The interesting detail is the relationship to the rest of the library. An EnumClass is a class, so it can be a field of a value type, and it can be a field of a Built Collection, and the serialisation support covers enum classes as well as value types. That means the three features compose rather than sitting side by side, which is the difference between a set of features and a model.
Serialisation that keeps the object model intact
Built Values comes with JSON serialisation support that allows you to serialise a complete data model of Built Values, Enum Classes and Built Collections, and the chat example shows what that makes possible: building a full application with Dart on the server and the client.
The design goal is stated as fully supporting object oriented design, with any object model that you can design able to be serialised, including full use of generics and interfaces. The comparison is with other libraries, some of which require concrete types or do not fully support generics.
That comparison is the one to weigh, because it is the difference between a serialiser that handles your model and one that handles the subset of your model that somebody thought of. A model with an interface-typed field, a generic field, or a polymorphic field is exactly where the concrete-type libraries stop working, and exactly the kind of model a shared client and server codebase ends up with.
The chat example is the end-to-end demonstration, and the documentation notes that the data model used on both client and server side uses value types, enums and serialisation from built_value. The same model definition is therefore the contract between the two sides, which is the point of the library in a client and server application: one Dart definition, serialised identically in both directions.
There is also a tutorial on creating custom serialisers, which covers the case where the generated serialiser is not the one you want for a particular field.
The generation step, and what it demands from your build
The build integration is the part of the library that touches your project configuration, and the documentation is specific about it.
Codegen is done using:
dart run build_runner buildfor one-off builds, and to continuously watch your source and update the generated output when it changes you use:
dart run build_runner watchNeither works on its own. You need a dev dependency on built_value_generator and build_runner, and the documentation points at the example pubspec.yaml as the reference for how that is set up.
So the cost of adopting this library is a build step. Every clone of your project, every CI job and every developer machine runs the generator, and a stale generated file is a real failure mode: the field you added is not in the equality, and the bug appears far from the cause, exactly as the documentation warned about hand-written value types.
The mitigation is the watch mode, and it is the reason the generator is part of the development loop rather than a script you run before a release.
For a Flutter project the same arrangement applies, since the generator is language level rather than framework level, and the examples in the repository, including the simple example, the chat example and the end to end test, all show the same pattern of a library package, a generator package, a test package and an example.
The tooling around it, and the licence and maintenance picture
The repository collects a surprising amount of community content around a small core library, and it is worth knowing what is in it before you decide to build your own.
There are converters, one that turns JSON into a Dart built_value class and another that turns a JSON or JavaScript object into one, both as web tools. There are editor extensions, a VSCode extension and an IntelliJ plugin, both for built_value snippets. There are tutorials for custom serialisers and for building a Flutter plus built_value application with Reddit, with video and source code. And there is a list of articles, including pieces on built_value for immutable object models, for serialisation, on building a chat app in Dart, and on end to end testing.
The end to end test directory in the repository is a good sign for a library whose value is equality semantics, because the thing you want tested is that two objects with the same fields are equal after a round trip through serialisation.
The licence is BSD-3-Clause, with the LICENSE file at the root of the repository, which is permissive and requires only that the notice travels with copies. The project is not archived, the last push was on 2026-09-02, and the release history is regular: v8.13.0 for support of a new constructor syntax, v8.12.7 to allow analyzer 14, and v8.12.6 to require analyzer 13.0.0.
Those release notes are the practical detail to read before you adopt. The package pins the analyzer version it works with, and the sequence of releases in 2026 is largely about keeping that pin current, which means your Dart and analyzer versions are part of the compatibility surface.
Where built_value is the wrong tool is mostly about scale of the problem. A data class that never leaves a widget, never crosses a process boundary and is never compared by value does not need a generator, and the build step would be pure overhead. The library earns its cost exactly where the documentation says the problem is, in classes that are sent over RPC or stored, where a missing field in an equality check is a bug you will spend an afternoon on.
Editorial conclusion
Adopt built_value if your Dart or Flutter project has domain objects that are compared by value, sent over RPC or persisted, and you would rather generate the equality, the builder and the serialiser than write them. Do not adopt it for simple data classes that never leave a widget, because the generator and the build step cost more than the boilerplate you would have written. Before you commit, check which analyzer version your build pins, since the release notes show the package requiring specific analyzer versions, and read the example pubspec.yaml to see the dev dependency setup the generator expects.
Frequently asked questions
What does built_value provide in Dart?
It provides immutable value types, EnumClass, which are classes that behave like enums, and JSON serialization, with immutable collections coming from the related built_collection package. The boilerplate for equality, the builder and the serialiser is generated for you.
How do I generate the built_value code?
Run dart run build_runner build for a one-off build, or dart run build_runner watch to continuously watch your source and update the generated output. You need a dev dependency on built_value_generator and build_runner, as shown in the example pubspec.yaml.
Why use built_value instead of writing value classes by hand?
The documentation is direct that creating and maintaining value types by hand requires a lot of boilerplate, that it is boring to write, and that a mistake very likely creates a bug that is hard to track down. Code generation derives the equality and the rest from the field list, so the two cannot drift apart.
What does EnumClass add over a Dart enum?
Dart's enums fall short for object oriented design, and EnumClass fills the gap: the constants have name and toString, can be used in switch statements, are real classes that can hold code and implement interfaces, there is a generated values method returning all values in a BuiltSet, and a generated valueOf method that takes a String.
What licence is built_value released under, and which version should I use?
BSD-3-Clause, with the licence text in the LICENSE file at the root of the repository. The latest release is v8.13.0 from 2026-09-02, and the recent release notes show the package tracking the analyzer version, requiring analyzer 13.0.0 in v8.12.6 and allowing analyzer 14 in v8.12.7, so check that against your Dart toolchain.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/google-built-value-dart)Was this useful?