← Back to Blog

Fix 'output-dir option is required' in Flutter l10n

flutterl10ni18ngen-l10nmigration

Fix 'output-dir option is required' in Flutter l10n

You pulled the latest stable Flutter SDK, ran flutter run, and your app — which built fine last week — now dies before it even compiles:

User defined the synthetic-package option to false but did not
specify an output directory. The output-dir option is required
when synthetic-package is false.

Nothing in your Dart changed. What changed is Flutter. The synthetic package:flutter_gen that used to hold your generated AppLocalizations has been removed, and the gen-l10n tool now refuses to guess where to put the generated code. This is not a bug in your project — it's the final step of a deprecation that started back in November 2024. The good news: fixing it is a three-line config edit and one find-and-replace. Here's exactly what to change.

What actually broke

For years, flutter gen-l10n wrote your generated localization classes into a synthetic package at .dart_tool/flutter_gen/gen_l10n/. You never saw those files — they lived in a virtual package you imported like this:

import 'package:flutter_gen/gen_l10n/app_localizations.dart';

That approach caused a lot of pain: broken IDE navigation, confused analyzers, incompatibility with Dart's explicit-package-dependencies and pub workspaces, and code you couldn't open or read. So the Flutter team deprecated it. The rollout happened in stages:

  • Nov 2024 — deprecation announced; synthetic-package: false became the recommended setting.
  • Flutter 3.32 (2025 stable) — the synthetic package stopped being generated by default. generate: true in pubspec.yaml became required.
  • 2026 stablepackage:flutter_gen support is fully removed. The tool no longer creates a synthetic package or edits your package_config.json at all.

Once flutter_gen is removed, gen-l10n has only one place to write: your lib/ source tree. But it won't invent a directory for you — hence the error. flutter synthetic-package false without an output-dir is now an incomplete configuration.

The 3-line fix: minimal l10n.yaml

Open l10n.yaml in your project root (create it next to pubspec.yaml if you don't have one). The three lines that resolve the error are synthetic-package: false, an explicit output-dir, and — recommended for a clean migration — nullable-getter: false. Here's a complete, correct file:

# l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
output-class: AppLocalizations

# The three lines that fix the breaking change:
synthetic-package: false      # stop using package:flutter_gen
output-dir: lib/l10n          # where AppLocalizations now lands
nullable-getter: false        # AppLocalizations.of(context) is non-null

A few notes so you don't trip on the details:

  • output-dir can equal arb-dir. Putting the generated Dart right next to the .arb files (both in lib/l10n) is the most common, tidy layout. You can also send them elsewhere, e.g. output-dir: lib/src/generated/l10n.
  • nullable-getter: false makes AppLocalizations.of(context) return a non-nullable instance, so you write AppLocalizations.of(context).title instead of AppLocalizations.of(context)!.title. It's optional, but if you're editing this file anyway, flip it now and delete a pile of ! operators.
  • output-class and output-localization-file default to AppLocalizations and app_localizations.dart. They're shown here for clarity; keep whatever names you already use.

Then make sure your pubspec.yaml still opts into generation — this has been required since 3.32:

# pubspec.yaml
flutter:
  generate: true

Where the generated AppLocalizations file lands now

Before, the file was invisible inside .dart_tool/. Now, after you run:

flutter gen-l10n

you get real, committable source files at your output-dir:

lib/l10n/
├── app_en.arb                 # your source strings
├── app_es.arb
├── app_localizations.dart     # generated: base class + delegate
├── app_localizations_en.dart  # generated: English
└── app_localizations_es.dart  # generated: Spanish

These are ordinary Dart files. You can open them, cmd-click into them, and — importantly — decide whether to commit them. Most teams add the generated app_localizations*.dart files to .gitignore and regenerate on build (they're produced automatically by flutter run/flutter build when generate: true). Either choice is fine; just be consistent across the team.

Migrate every import in one command

The last step is the one that touches the most files. Every import 'package:flutter_gen/...' is now a dead reference and will fail to resolve. You need to point them at your real package path. The new import is package:<your_package_name>/<output-dir-under-lib>/app_localizations.dart. If your package is named my_app (check the name: field in pubspec.yaml) and output-dir: lib/l10n, the import becomes:

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

Instead of editing dozens of files by hand, do a project-wide find/replace. On macOS/BSD:

grep -rl 'package:flutter_gen/gen_l10n' lib test \
  | xargs sed -i '' 's|package:flutter_gen/gen_l10n/app_localizations.dart|package:my_app/l10n/app_localizations.dart|g'

On Linux (GNU sed), drop the empty quotes after -i:

grep -rl 'package:flutter_gen/gen_l10n' lib test \
  | xargs sed -i 's|package:flutter_gen/gen_l10n/app_localizations.dart|package:my_app/l10n/app_localizations.dart|g'

Replace my_app with your actual package name and l10n with your output-dir path relative to lib/. If you import other generated files (rare), repeat for those paths.

Verify the migration

Run these in order and you should be back to green:

flutter clean
flutter pub get
flutter gen-l10n     # regenerates into output-dir
flutter analyze      # no unresolved flutter_gen imports
flutter run

If flutter analyze still flags a missing flutter_gen import, you have a stray reference the grep missed — search again for flutter_gen across the whole repo, including any code under packages/ in a monorepo or workspace.

Common follow-on errors

  • "Attempted to generate localizations code without having the flutter: generate flag turned on." — Add generate: true under flutter: in pubspec.yaml.
  • Using pub workspaces? With synthetic-package: false and workspaces you may need explicit-package-dependencies enabled; the non-synthetic path is exactly what makes workspaces work, so this is the direction you want anyway.
  • AppLocalizations.of(context) now needs a ! — you left nullable-getter at its default (true). Set it to false and regenerate, or add the null-assertion.

Turn a scary breaking change into a config you understand

The reason this error feels alarming is that gen-l10n used to hide everything. Moving generation into your source tree is a net win: readable code, working navigation, and a config file you can reason about. If you want the full field-by-field reference for tuning this file — deferred loading, headers, untranslated-message reports — see our complete l10n.yaml configuration guide.

Managing the .arb files those generated classes read from is its own chore once you pass two or three locales. FlutterLocalisation gives you a visual ARB editor so you edit app_<locale>.arb translations in a UI instead of hand-diffing JSON, plus ICU plural-syntax validation that flags a locale missing a plural category its language actually requires (a dropped few/many in Arabic, Polish, or Russian). Once your build is green again, keep it that way.

Try FlutterLocalisation free and manage your Flutter translations without touching raw ARB by hand.