← Back to Blog

Fix flutter_gen Not Found: Migrate gen-l10n on Flutter 3.32+

fluttergen-l10nflutter_genl10n.yamlmigrationci

Fix flutter_gen Not Found: Migrate gen-l10n on Flutter 3.32+

You upgraded to Flutter 3.32 or later, opened the project, and every file that imports package:flutter_gen/gen_l10n/app_localizations.dart is now red. flutter analyze says "Target of URI doesn't exist", CI fails on the same line, and nothing in your code changed. The cause is a tool change, and the fix is a short migration you only do once.

What actually changed in Flutter 3.32

Before 3.32, flutter gen-l10n wrote your AppLocalizations class into a synthetic package. The tool created a fake flutter_gen package under .dart_tool/ and rewrote package_config.json so the import resolved. That package never appeared in pubspec.yaml, which confused the analyzer, dart fix, build_runner and anyone reading the repo.

The breaking change notice landed in 3.28 pre-releases and shipped in the 3.32.0 stable line. From that version the default flipped: generated localizations are written into your real lib/ tree, and the synthetic package is no longer created by default. Newer stable releases go further. Setting synthetic-package: true now produces a hard error:

l10n.yaml: Cannot enable "synthetic-package", this feature has been removed.
See http://flutter.dev/to/flutter-gen-deprecation.

So there is no going back. The only path is source-generated AppLocalizations.

Step 1: the l10n.yaml you want

Open l10n.yaml at the project root (create it if your project never had one) and make it look like this:

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-dir: lib/l10n
synthetic-package: false
nullable-getter: false

Three lines matter for the migration.

synthetic-package: false is the explicit opt-out. On 3.32 and 3.35 it is already the default, but writing it down keeps the behaviour identical for a teammate still on an older SDK. On the newest stables the key is accepted with false and rejected with true; if flutter gen-l10n prints that the argument "no longer has any effect and should be removed", just delete the line.

output-dir is where the Dart files go. If you leave it out, the tool writes next to your ARB files in arb-dir. Keeping both at lib/l10n is the simplest layout and matches the official docs. Some teams prefer lib/src/generated/l10n so the generated code is visibly separate from the hand-written ARB files. Both work; pick one and keep it.

nullable-getter: false is optional but worth doing while you are here. It makes AppLocalizations.of(context) return a non-null value so you can drop every ! after it.

In pubspec.yaml, generate: true is still required. It is what makes flutter run and flutter build regenerate the files automatically:

flutter:
  generate: true

If an old dart fix run left a flutter_gen: any entry under dependencies, remove it. That was a known bug and the fake dependency breaks resolution on its own.

Step 2: generate once, then rewrite the imports

Clear the stale synthetic package and generate the real files:

flutter clean
flutter pub get
flutter gen-l10n

You should now have lib/l10n/app_localizations.dart plus one app_localizations_<locale>.dart per language. Now rewrite every import in one go. The new path uses your real package name from pubspec.yaml, shown here as my_app:

# macOS (BSD sed needs the empty '' after -i)
grep -rl "package:flutter_gen/gen_l10n/app_localizations.dart" lib test \
  | xargs sed -i '' 's#package:flutter_gen/gen_l10n/app_localizations.dart#package:my_app/l10n/app_localizations.dart#g'

# Linux (GNU sed)
grep -rl "package:flutter_gen/gen_l10n/app_localizations.dart" lib test \
  | xargs sed -i 's#package:flutter_gen/gen_l10n/app_localizations.dart#package:my_app/l10n/app_localizations.dart#g'

If you chose a different output-dir, change the replacement path to match. Then run flutter analyze. If the IDE still shows red squiggles after the analyzer passes, restart the Dart analysis server; it caches the old package_config.json.

The wiring in MaterialApp does not change:

import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:my_app/l10n/app_localizations.dart';

MaterialApp(
  localizationsDelegates: const [
    AppLocalizations.delegate,
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: AppLocalizations.supportedLocales,
  home: const HomePage(),
);

Step 3: commit or ignore the generated files?

This is the one real decision in the migration, and the Flutter team was explicit in the deprecation announcement: the intent is to "push users into checking the generated files into their application".

Commit them if you want git clone followed by flutter run to work with no extra step, if your CI runs flutter analyze or tests before any build, or if you rely on IDE navigation into AppLocalizations without running the generator first. This is the default recommendation and what most apps should do. The diff noise is small: the files only change when an ARB file changes.

Ignore them if you treat all generated code as build output and already have a pre-build or pre-analyze step. Then add this to .gitignore and make sure every CI job calls flutter gen-l10n before analysis:

# Generated by flutter gen-l10n
lib/l10n/app_localizations.dart
lib/l10n/app_localizations_*.dart

Do not mix the two. A half-committed set of locale files is the most common source of "works on my machine" after this migration.

Step 4: a CI check that blocks the old import

Once the team is migrated, someone will paste a snippet from an old tutorial and reintroduce package:flutter_gen. The analyzer catches it on their machine, but only if they run it. Make the pipeline refuse the change outright:

# .github/workflows/flutter.yml (step inside your existing job)
- name: Block deprecated flutter_gen imports
  run: |
    if grep -rn "package:flutter_gen/" lib test; then
      echo "::error::Found import of the removed synthetic flutter_gen package."
      echo "Use package:my_app/l10n/app_localizations.dart instead."
      exit 1
    fi

- name: Generate localizations
  run: flutter gen-l10n

- name: Analyze
  run: flutter analyze --fatal-infos

The grep step costs nothing and gives a clear message instead of a wall of unresolved-URI errors. If you ignore generated files, the gen-l10n step must stay before analyze and before tests. If you commit them, add a second guard that fails when the committed output is stale:

- name: Generated l10n is up to date
  run: |
    flutter gen-l10n
    git diff --exit-code -- lib/l10n

Keep the ARB files clean while you move

The migration touches only Dart output. Your app_en.arb, app_de.arb and friends stay exactly where they were, and that is the moment to tidy them, because flutter gen-l10n is now stricter about what it will accept and the errors surface on every build rather than only at runtime.

Two things bite most often during this move. First, a locale file that is missing keys present in the template. The generator still builds, but the untranslated strings fall back silently. Add untranslated-messages-file: untranslated.json to l10n.yaml and the tool writes a per-locale list you can hand to translators. Second, broken ICU plurals: a Polish or Arabic file where someone copied the English one/other form and dropped few and many. That compiles and then shows the wrong string in production.

The FlutterLocalisation ARB editor is built for exactly this. You edit each app_<locale>.arb in a UI instead of raw JSON, see every locale side by side so missing keys are obvious, and its ICU plural validation flags a locale that lacks a plural category the language actually needs. Run your ARB files through it once before the final flutter gen-l10n, commit the output, and the generated AppLocalizations you check in is the clean one. The free tier covers a small app; see pricing for larger teams.

If you only need the quickest unblock and not the full migration, the shorter post on fixing the flutter_gen import error covers just the import swap. More Flutter i18n guides are on the blog.

Try FlutterLocalisation free and get your ARB files validated before the next flutter gen-l10n run.