Code Generation
35.1 build_runner + json_serializable
// lib/models/user.dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake)
class User {
final int id;
final String fullName;
@JsonKey(defaultValue: false)
final bool isAdmin;
final DateTime createdAt;
User({required this.id, required this.fullName, required this.isAdmin, required this.createdAt});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}dart pub add json_annotation
dart pub add --dev build_runner json_serializable
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch35.2 freezed (Immutable Data Classes and Unions)
Other generators: injectable (DI), retrofit/chopper (HTTP clients), drift (SQL), riverpod_generator, mockito (mocks), auto_route, built_value, dart_mappable.
Writing generators: source_gen + analyzer + the build package (builders, build.yaml).
Macros: the experimental macros feature was cancelled by the Dart team in early 2025. Focus moved to improving build_runner performance and language features (such as augmentations and data-class work). Code generation remains the standard approach.
import 'package:freezed_annotation/freezed_annotation.dart';
part 'todo.freezed.dart';
part 'todo.g.dart';
@freezed
abstract class Todo with _$Todo {
const factory Todo({
required String id,
required String title,
@Default(false) bool done,
}) = _Todo;
factory Todo.fromJson(Map<String, dynamic> json) => _$TodoFromJson(json);
}
void main() {
final t = Todo(id: '1', title: 'Learn Dart');
final done = t.copyWith(done: true); // copyWith, ==, hashCode, toString generated
print(t == Todo(id: '1', title: 'Learn Dart')); // true
}