Dart null-safety migration: a task guide
A practical task guide for migrating Dart and Flutter projects to sound null safety using the official analysis and annotation workflow.
05 Jun 2026, 02:18 UTC

Migrate a Dart project to sound null safety
When you upgrade a Dart or Flutter project to null safety, the compiler stops accepting implicit null assignments, and code that previously compiled may now error. This is the actual problem many teams face when moving from pre-2.12 Dart to the sound null-safety model introduced in Dart 2.12. The useful takeaway is that you can migrate incrementally using the dart migrate command, interpret its diagnostics, and apply required, late, and ? annotations without a full, disruptive rewrite. This guide provides a task-focused procedure: desired outcome, necessary prerequisites, a focused procedure, expected checks, and relevant recovery options, so you can transition your codebase with controlled risk.
Check your tooling and project state
Ensure you have Dart 2.12 or later installed. The null-safety migration feature was introduced in that version and requires opt-in via the migration tool. Older Dart versions do not support the analysis commands. Additionally, verify that your flutter version aligns with your Dart version if you are working in a Flutter project, as tooling compatibility affects the migration experience. Check your pubspec.yaml for the current package null-safety status: look for a null-safety key or inspect the package's supported Dart SDK constraint. Note any dependencies that list a Dart version below 2.12, as these will block a full migration until they are updated or replaced.
Run the null-safety analysis
dart migrateOpen a terminal in your project root directory and run the dart migrate command. This performs a read-only analysis of your codebase and generates a report that categorizes each file into one of several states: fully null-safe, needs annotations, has errors, or is blocked by legacy dependencies. The command does not modify any files, so there is no rollback needed for this analysis phase. Review the output carefully; it lists line numbers, suggested annotation types, and a summary count of issues. If the report is large, focus on the highest-impact files first, as fixing those often resolves cascading warnings in dependent code.
Interpret the diagnostics
required: annotate function parameters, local variables, or instance fields that must never be null. The analyzer will enforce this at compile time, and calling code must provide a non-null argument.late: mark instance variables that are initialized after the constructor completes. Sound null safety allows assigning a late field once, after which it behaves like a non-nullable type.?: declare a type as nullable. You must handle the value explicitly-either with an if check, a ?? null-coalescing operator, or by propagating the nullability.
Each suggestion corresponds to a specific code pattern. The required annotation is common for factory constructors and public API parameters. The late annotation suits fields initialized later, such as those obtained from async operations. The ? type is necessary when a variable can legitimately be null and you plan to handle it at runtime.
Update dependencies safely
If the analysis flags a dependency without null safety, you have two practical paths. First, upgrade the dependency to a null-safe version by bumping its version constraint in pubspec.yaml and consulting the package's changelog for breaking changes. Second, if an upgrade is not immediately possible, keep the package in legacy mode and use the dart analyze command with the --no-null-safety flag to exclude it from strict enforcement. Some teams also use conditional compilation or source-level guards to isolate legacy code during the transition. Always test the upgraded dependency's compatibility before committing, as version bumps can introduce API changes.
Verify with tests
dart analyze --null-safety | Zero null-safety warnings |
|---|---|
dart test | All tests pass, no null-related failures |
Run dart analyze --null-safety and confirm that the output reports no warnings or errors related to null safety. The command scans your code using the annotations you've applied and the package's null-safety status. If warnings appear, revisit the suggested annotations or adjust the type promotions. Next, run dart test (or flutter test for Flutter projects) and ensure every test suite passes without null-related failures. Look particularly for tests that exercise null propagation, optional chaining, and uninitialized fields, as these are the areas where sound null safety makes previously hidden errors visible.
Recovery and rollback
The dart migrate command is read-only and never changes state, so no rollback is needed for the analysis step. If you apply annotations and later break your build-for example, by over-applying required to a parameter that can null-use version control to revert the annotation changes or temporarily remove the annotations while you debug. Commit annotation edits separately from dependency version bumps so you can isolate which change caused a regression. If a dependency upgrade introduces breaking API changes, consult the package documentation and consider pinning the old version until a compatible release is available.
After annotating your code and updating dependencies, run the full test suite on your target platform. For Flutter projects, also run flutter test on a device or emulator to catch any platform-specific null issues. Document the migration status in your repository's README: note which packages are fully null-safe, which are still in legacy mode, and any known caveats. This documentation helps future contributors understand the project's null-safety state and avoids accidental regressions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.