Irbisa · Cheatsheet
Code Generation & build_runner
build_runner, part files, json_serializable, freezed, build.yaml, source_gen, build times
What is
build_runner, and why does Dart generate code at build time instead of using runtime reflection?Why does a
json_serializablemodel needpart 'user.g.dart';and the forwarders to_$UserFromJson?import 'package:json_annotation/json_annotation.dart';import 'address.dart'; // the part file cannot import anything itself part 'user.g.dart'; // you write this; the generator writes `part of` @JsonSerializable()…What is the difference between
build_runner build,watchandclean, and what does--delete-conflicting-outputsdo today?# one-off generation: after a pull, a dependency upgrade, in CIdart run build_runner build # day-to-day: stays running, rebuilds only what changeddart run build_runner watch…How do you control key names and enum values with
json_serializable:snake_case, one renamed field, a skipped field, an unknown enum value?enum Status { @JsonValue('in_progress') inProgress, done, unknown,}…Should generated
.g.dartand.freezed.dartfiles be committed to git?What does
print(order.toJson())show here, and why can that break code that does not go throughjsonEncode?@JsonSerializable()class Order { const Order(this.id, this.address); final String id; final Address address; // Address is @JsonSerializable too Map<String, dynamic> toJson() => _$OrderToJson(this);} void main() { final order = Order('42', const Address('Almaty')); print(order.toJson()); print(jsonEncode(order.toJson()));}What happens when each of these three payloads is passed to the generated
Profile.fromJson?@JsonSerializable()class Profile { const Profile({required this.name, required this.age, this.bio}); final String name; final int age; final String? bio; factory Profile.fromJson(Map<String, dynamic> json) => _$ProfileFromJson(json);} Profile.fromJson({'name': 'Ann', 'age': 30.0}); // 1Profile.fromJson({'name': null, 'age': 30}); // 2Profile.fromJson({'name': 'Ann', 'age': '30'}); // 3How do you serialize a type
json_serializablecannot handle, such as an epoch-millis timestamp or aMoneyvalue object?class Money { const Money(this.cents); final int cents; // minor units, never a double} class MoneyConverter implements JsonConverter<Money, int> {…Find the bug: a user edits only their name on the profile screen, and their bio and avatar disappear on the server.
// PATCH /me: the server updates only the keys present in the body@JsonSerializable(fieldRename: FieldRename.snake)class ProfilePatch { const ProfilePatch({this.name, this.bio, this.avatarUrl}); final String? name; final String? bio; final String? avatarUrl; Map<String, dynamic> toJson() => _$ProfilePatchToJson(this);} // Edit profile screen: the user changed only the nameFuture<void> rename(Dio dio, String name) => dio.patch<void>('/me', data: ProfilePatch(name: name).toJson());What does
freezedgenerate for you, and when is a plainsealedclass or@JsonSerializableenough?import 'package:freezed_annotation/freezed_annotation.dart'; part 'user.freezed.dart'; // ==, hashCode, toString, copyWithpart 'user.g.dart'; // fromJson / toJson, from json_serializable @freezed…This model compiled with freezed 2, but after upgrading to freezed 3 it fails with "Missing concrete implementations". What changed?
import 'package:freezed_annotation/freezed_annotation.dart'; part 'point.freezed.dart'; @freezedclass Point with _$Point { const factory Point({required double x, required double y}) = _Point;} // error: Missing concrete implementations of// 'getter mixin _$Point on Object.x' and 'getter mixin _$Point on Object.y'.Find the bug: the round-trip tests for these events pass, but in production every
screen_viewevent fails to decode.// Server events: {"type": "purchase", "sku": "A1", "cents": 990}// {"type": "screen_view", "route": "/home"}@Freezed(unionKey: 'type')sealed class Event with _$Event { const factory Event.purchase({required String sku, required int cents}) = Purchase; const factory Event.screenView({required String route}) = ScreenView; factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);}What is
build.yamlused for, and how do you limit which files a builder processes?# build.yaml, next to pubspec.yamltargets: $default: builders: json_serializable: generate_for:…You add a string to an ARB file and run
build_runner, butAppLocalizationsgets no new getter. Why?# l10n.yaml at the project root: read by `flutter gen-l10n`, not build_runnerarb-dir: lib/l10n/arbtemplate-arb-file: app_en.arboutput-dir: lib/l10n # real source files, imported normallyoutput-localization-file: app_localizations.dartoutput-class: AppLocalizations…A full
build_runnerbuild takes several minutes. Where does the time go, and how do you bring it down?How would you write your own code generator with
source_gen, and how doesbuild_runnerpick it up?import 'package:analyzer/dart/element/element.dart';import 'package:build/build.dart';import 'package:source_gen/source_gen.dart';import 'package:my_annotations/my_annotations.dart'; // AnalyticsEvent class EventGenerator extends GeneratorForAnnotation<AnalyticsEvent> {…In a monorepo, why does
build_runnergenerate nothing for a feature package, and how do you set up generation across packages?# pubspec.yaml at the repo root: a pub workspacename: shop_workspaceenvironment: sdk: ^3.9.0workspace: - app…What happened to Dart macros, and what does that mean for code generation in your project?