CalcSnippets
Mobile Development 5 min read

Make `build_runner` Output a Deliberate Part of Flutter Builds

Generated Dart files must be reproducible from declared builders and inputs. Decide whether they are committed, then make CI enforce that choice.

Know what owns each generated file

Flutter projects commonly use generators for serializers, dependency injection, routes, and immutable models. A generated file is not disposable noise if the source imports it. The repository needs a clear rule: either generated output is committed and checked for drift, or every build regenerates it before compilation.

For a committed-output workflow, run:

dart run build_runner build --delete-conflicting-outputs
git diff --exit-code -- 'lib/**/*.g.dart'

The diff check catches a developer who changed an annotation but forgot to regenerate output. Use a glob that matches the project's generated-file policy rather than checking every Dart file indiscriminately.

Treat conflicts as ownership questions

--delete-conflicting-outputs resolves collisions when a builder owns the output path. Before using it, confirm that no hand-written file shares that generated name. If two builders claim the same output, fix the generator configuration instead of rerunning deletion until the error goes away.

When output is not committed, CI must run code generation before flutter analyze and tests. A local IDE plugin that silently generates files is not a reproducible build step. Print the generator command in the repository's developer docs and pin the generator package through pubspec.lock.

If generated diffs are unexpectedly large, compare the Dart SDK, generator versions, and line endings first. A huge reformat may indicate toolchain drift rather than a meaningful source change. Review generated changes with their input annotations and keep the generator invocation deterministic so a future build does not churn the same files again.

Choose a policy for committed output

Committed generated files are convenient when consumers or tooling need them immediately, when generated output is part of the package interface, or when regeneration would otherwise be unavailable. The tradeoff is review noise and the risk that source and generated output drift apart. If you commit output, make the generator version explicit, regenerate in a dedicated step, and fail CI when the resulting diff is non-empty. Review both the source model and generated code when behavior changes; a checked-in file is not automatically correct just because the generator produced it.

For build-time generation, keep the generator and its configuration in the dependency graph and invoke it before any command that imports the output. A clean checkout is essential for validating this policy. Developers often have stale generated files on their machines, so a local build may pass even though a fresh CI runner cannot compile the project. Add a CI job that starts from a clean checkout, runs generation, and then performs static analysis and tests.

Make generation deterministic and reviewable

Pin generator dependencies through the lockfile and avoid relying on globally installed binaries. If generation output depends on current time, machine paths, or unordered filesystem enumeration, fix those inputs or normalize the output so repeated runs produce a stable diff. A generator that rewrites unrelated files on every run is difficult to review and will eventually teach contributors to ignore generated changes.

The --delete-conflicting-outputs option is useful when stale files block a deliberate regeneration, but it should not become a command people run without inspecting the affected files. Before adopting it in CI, verify that the generator owns the directories it can delete and that hand-written files cannot share the same naming pattern. Run generation twice and confirm the second run is clean. That simple idempotence check catches unstable timestamps and configuration-order issues.

When generated code is missing, diagnose the owner before editing the generated file directly. Find the source annotation, schema, or builder configuration that should produce it, then run the documented generator. If the result is wrong, fix the input or generator configuration. A manual edit to generated output will be overwritten on the next build and can leave the repository apparently healthy while the real source remains broken.

Choose a policy for committed output

Committed generated files are convenient when consumers or tooling need them immediately, when generated output is part of the package interface, or when regeneration would otherwise be unavailable. The tradeoff is review noise and the risk that source and generated output drift apart. If you commit output, make the generator version explicit, regenerate in a dedicated step, and fail CI when the resulting diff is non-empty. Review both the source model and generated code when behavior changes; a checked-in file is not automatically correct just because the generator produced it.

For build-time generation, keep the generator and its configuration in the dependency graph and invoke it before any command that imports the output. A clean checkout is essential for validating this policy. Developers often have stale generated files on their machines, so a local build may pass even though a fresh CI runner cannot compile the project. Add a CI job that starts from a clean checkout, runs generation, and then performs static analysis and tests.

Make generation deterministic and reviewable

Pin generator dependencies through the lockfile and avoid relying on globally installed binaries. If generation output depends on current time, machine paths, or unordered filesystem enumeration, fix those inputs or normalize the output so repeated runs produce a stable diff. A generator that rewrites unrelated files on every run is difficult to review and will eventually teach contributors to ignore generated changes.

The --delete-conflicting-outputs option is useful when stale files block a deliberate regeneration, but it should not become a command people run without inspecting the affected files. Before adopting it in CI, verify that the generator owns the directories it can delete and that hand-written files cannot share the same naming pattern. Run generation twice and confirm the second run is clean. That simple idempotence check catches unstable timestamps and configuration-order issues.

When generated code is missing, diagnose the owner before editing the generated file directly. Find the source annotation, schema, or builder configuration that should produce it, then run the documented generator. If the result is wrong, fix the input or generator configuration. A manual edit to generated output will be overwritten on the next build and can leave the repository apparently healthy while the real source remains broken.

Keep reading

Related guides