We’re missing a way to actually specify values for the fields. We could think about using named optional parameters:

factory User({String name, String nickname}) = _$User;

But this has a couple of drawbacks: it forces you to repeat all the field names in the constructor, and it only provides a way to set all the fields in one go; what if you want to build up a value piece by piece?

Fortunately, the builder pattern comes to the rescue. We’ve already seen how well it works for collections in Dart — thanks to the cascade operator. Assuming we have a builder type, we can use that for the constructor — by asking for a function that takes a builder as a parameter:

abstract class User {

String get name; @nullable

String get nickname; User._();

factory User([updates(UserBuilder b)]) = _$User;

}

That’s a bit surprising, but it leads to a very simple syntax for instantiation:

var user1 = new User((b) => b

..name = 'John Smith'

..nickname = 'Joe');

What about creating new values based on old ones? The traditional builder pattern provides a “toBuilder” method to convert to a builder; you then apply your updates and call “build”. But a nicer pattern for most use cases is to have a “rebuild” method. Like the constructor, it takes a function that takes a builder, and provides for easy inline updates:

var user2 = user.rebuild((b) => b

..nickname = 'Jojo');

We do still want “toBuilder”, though, for cases when you want to keep a builder around for a little while. So we want two methods for all our value types:

abstract class Built<V, B> {

// Creates a new instance: this one with [updates] applied.

V rebuild(updates(B builder)); // Converts to a builder.

B toBuilder();

}

You don’t need to write the implementation for these, built_value will generate it for you. So you can just declare that you “implement Built”:

library user; import 'package:built_value/built_value.dart'; part 'user.g.dart'; abstract class User implements Built<User, UserBuilder> {

String get name; @nullable

String get nickname; User._();

factory User([updates(UserBuilder b)]) = _$User;

}

And that’s it! A value type defined, an implementation generated and easy to use. Of course, the generated implementation isn’t just fields: it also provides “operator==”, “hashCode”, “toString” and null checks for required fields.