skip to content

When upgrading a large Dart or Flutter codebase to a new SDK, how do you use dart fix to migrate deprecated APIs safely?

level: seniorimportance: should knowfreq 35%

answer

  1. preview before you write
  2. only diagnostics that have fixes
  3. data-driven fixes shipped with packages
  4. --code narrows to one diagnostic
  5. review the diff, then analyze

basics

~20 s

Run dart fix --dry-run to list proposed fixes per file, then dart fix --apply, optionally narrowed with --code to one diagnostic. It fixes diagnostics that have automated fixes, including data-driven API migrations that SDKs and packages ship.

solid answer

~50 s

`dart fix` asks the analysis server for every diagnostic in the target that has an automated fix and applies them in bulk. It needs a mode: `--dry-run` (`-n`) lists proposed fixes by file and code; `--apply` writes them; with neither it only prints usage. Beyond compile errors and enabled lints, it applies **data-driven fixes**: SDKs and packages ship `fix_data.yaml` rules that rename or rewrite deprecated APIs, which is how SDK upgrades migrate call sites. On a large codebase: upgrade the SDK and run `dart pub get`, run `--dry-run` and read the counts, apply on a clean branch - scoping with `--code=<diagnostic>` to review one migration at a time - then run the analyzer and tests and commit the fix as its own change. Only enabled lints produce fixes, so enabling a lint can be a deliberate way to migrate a pattern.

code

bash · 5 lines
bash
dart pub get
dart fix --dry-run
dart fix --apply --code=deprecated_member_use
dart analyze
dart format .

go deeper

for a junior

Recall the two modes: --dry-run to preview and --apply to write the fixes.

for a middle

Explain which diagnostics produce fixes, what --code does and where data-driven migration fixes come from.

for a senior

Show a migration plan: upgrade and resolve, preview, apply per code in reviewable commits, then analyze, test and format.

for a principal

Decide how the organisation sequences SDK upgrades across packages and uses lints plus dart fix to keep them cheap.

## What dart fix does `dart fix` is the bulk version of an IDE quick fix. It starts the analysis server on the target directory, collects every **diagnostic that has an associated automated fix**, and applies those fixes across the codebase. It covers two families of problems: - **Analysis issues** reported by the analyzer - compile errors, warnings and the **lints you have enabled** in `analysis_options.yaml` - where a fix exists. - **Outdated API usage** after an SDK or package upgrade, through **data-driven fixes**. Not every diagnostic has a fix, and lints that are not enabled produce nothing to fix. ## The two modes | Command | Effect | |---|---| | `dart fix --dry-run` (`-n`) | computes fixes and prints a summary per file and diagnostic code; writes nothing | | `dart fix --apply` | computes and writes the fixes | | `dart fix --apply --code=a,b` | applies only fixes for the named diagnostic codes | | `dart fix` (no mode) | prints usage and exits | The tool runs several passes (up to four), because applying one fix can reveal another. At the end of a dry run it prints the exact `--apply` command to run, per code or for everything. ## Data-driven fixes: how SDK migrations reach your code API authors can describe renames and signature changes in **`fix_data.yaml`** files shipped with the library. The Dart SDK carries one for its own libraries - for example the rename of the old upper-case `FileMode` constants - and Flutter ships a `lib/fix_data/` directory covering many deprecations across its libraries. When your code uses an element one of those rules matches, the analyzer reports it and `dart fix` rewrites it. A recent example: Flutter's move of Material and Cupertino into standalone packages is migrated with `dart fix --apply --code=migrate_design_widgets`, and Dart 3.13.1 fixed that fix to rewrite `export` as well as `import` URIs. ## A safe migration on a large codebase 1. **Start clean.** Commit or stash other work so the fix diff stands alone. 2. **Upgrade and resolve.** Change the SDK or package constraint and run `dart pub get`, so the analyzer sees the new APIs and their fix data. 3. **Preview.** Run `dart fix --dry-run` and read which codes dominate and how many files each touches. 4. **Apply in slices.** Use `--code=<diagnostic>` to apply one migration at a time, so each commit has one purpose a reviewer can check. 5. **Verify.** Run the analyzer and the test suite; automated fixes are mechanical and can still change behaviour at edges, such as a renamed parameter with a different default. 6. **Format.** Run `dart format` afterwards so the committed result matches the formatter's output. 7. **Handle the rest by hand.** Some deprecations have no fix; the analyzer still lists them. In a monorepo, run it per package so each package's own analysis options and dependencies apply. ## Using lints as a migration lever Because fixes only exist for reported diagnostics, you can enable a lint in `analysis_options.yaml` on purpose - the dart.dev example is `use_super_parameters` - run `dart fix --apply`, and keep the lint so the analyzer flags regressions afterwards. ## Common mistakes - Running `--apply` first on a dirty tree, mixing mechanical and manual changes. - Expecting `dart fix` to fix lints that are not enabled. - Skipping `dart pub get` after upgrading, so the new fix data is not seen. - Assuming a clean `dart fix` run means the migration is complete.

  • Where do the API-migration fixes that dart fix applies after an SDK upgrade come from?
    From data-driven fix files shipped with the libraries: `fix_data.yaml` rules that describe renames and signature changes. The Dart SDK ships one for its libraries and Flutter ships a `lib/fix_data/` set, so upgrading and running `dart fix --apply` rewrites matching call sites.
  • What does dart fix do if you pass neither --dry-run nor --apply?
    It prints its usage information and exits without computing or writing any fixes. You must choose preview or apply explicitly.

saying these in an interview costs you the question

  • Believes dart fix fixes every analyzer diagnostic
  • Expects fixes for lints that are not enabled
  • Runs dart fix --apply on a dirty tree mixed with manual edits
  • Thinks a clean dart fix run means the migration is finished
  • Assumes dart fix applies changes when run with no flags