Flutter intl Version Conflict: Read the SDK's Pin First
You added a chart library, a PDF generator or a date picker, ran flutter pub get, and pub stopped dead:
Because every version of flutter_localizations from sdk depends on intl 0.20.2
and my_app depends on intl ^0.20.3, flutter_localizations from sdk is forbidden.
So, because my_app depends on flutter_localizations from sdk, version solving failed.
On an older SDK you get the same message with intl 0.19.0. Nothing is wrong with your code. flutter_localizations ships inside your Flutter SDK, and until very recently it pinned intl to a single exact version. An exact pin has no wiggle room: if anything in your dependency graph wants a different intl, resolution fails.
The fix is not to try random version numbers. It is to read the pin your installed SDK actually ships, then pick one of three paths deliberately.
Step 1 — Read the pin from your own SDK
Don't trust a Stack Overflow answer from 2024. Read the file on your disk:
# Resolve the real SDK root (works with fvm, asdf, brew, or a plain git clone)
FLUTTER_ROOT="$(dirname "$(dirname "$(readlink -f "$(command -v flutter)")")")"
grep -n 'intl' "$FLUTTER_ROOT/packages/flutter_localizations/pubspec.yaml"
# If readlink -f isn't available, copy the path printed by:
flutter doctor -v | head -n 3
# • Flutter version 3.47.0 on channel stable at /Users/you/fvm/versions/3.47.0
Typical output on a 3.44 SDK:
11: intl: 0.20.2
No caret. That is the entire problem — and the exact number you now build your pubspec around.
The pin, by Flutter stable release
Straight from the tagged packages/flutter_localizations/pubspec.yaml files in the Flutter repo:
| Flutter stable | flutter_localizations dependency |
Effect |
|---|---|---|
| 3.24 · 3.27 · 3.29 | intl: 0.19.0 |
Exact pin — only 0.19.0 resolves |
| 3.32 · 3.35 · 3.38 · 3.41 · 3.44 | intl: 0.20.2 |
Exact pin — only 0.20.2 resolves |
| 3.47 and later | intl: ^0.20.3 |
A real range — 0.20.3 and any future 0.20.x resolve |
Flutter 3.47 is the first stable release where this constraint became a caret range instead of a hard pin. If you are on it, most of these conflicts simply disappear. If you're below it, the pin is absolute.
For reference, the current intl release is 0.20.3 (CLDR v48, requires Dart 3.9+); 0.20.0–0.20.2 require Dart 3.3+. There is no 0.21.x yet, so "upgrade intl" always means one of those.
Step 2 — Find out what the new package actually wants
The error names the constraint, but not always the package that introduced it. Look at the resolved graph and the lockfile:
flutter pub deps -s list | grep -i intl # -s is short for --style
grep -A 3 '^ intl:' pubspec.lock
Then open the offending package's page on pub.dev and check its Dependencies tab. There are only two possibilities: it accepts your SDK's intl in some earlier version, or it doesn't and you need a newer Flutter.
Fix 1 — Constrain your app to the SDK's intl
This is the correct default, and the one to reach for first. Declare intl at exactly what the SDK ships:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: 0.20.2 # ← the number you grepped in Step 1. Use 0.19.0 on Flutter ≤ 3.29
flutter:
generate: true
Or let it follow the SDK automatically:
intl: any
any is fine for an application (it just yields to whatever flutter_localizations pins), but avoid it in a package you publish — pub.dev penalises unbounded constraints.
If the other package is what demands the newer intl, pin that package back to its last compatible release instead:
flutter pub add some_package:^3.10.0 # last version whose intl range includes 0.20.2
flutter pub get
One thing you can't do: drop intl altogether. If you use flutter gen-l10n, the generated app_localizations.dart imports package:intl/intl.dart for plurals and date placeholders, so intl has to stay in dependencies — constrained, not removed.
Fix 2 — Upgrade Flutter (the actual fix)
Because 3.47 replaced the pin with ^0.20.3, upgrading resolves the class of problem rather than one instance of it:
flutter upgrade # on the stable channel
flutter --version
flutter pub upgrade
After that, intl: ^0.20.3 in your own pubspec resolves cleanly alongside flutter_localizations. Budget time for the Flutter upgrade itself (Gradle/AGP, Xcode, deprecated APIs) — but you are trading a recurring dependency headache for a one-off migration.
Fix 3 — dependency_overrides (last resort)
When you're frozen on an SDK version and a critical package needs a newer intl:
dependency_overrides:
intl: 0.20.3
This works by disabling the resolver's safety check. Here is what that actually costs — the mechanics are visible in the SDK source, so this isn't hand-waving:
| What | What actually happens |
|---|---|
| Dart SDK floor | intl 0.20.3 requires Dart ^3.9. Override it on Flutter 3.32 or older and pub get fails outright with an SDK-constraint error — the override can't help. |
| Material/Cupertino dates | flutter_localizations does not read the overridden package's locale data. It calls initializeDateFormattingCustom() with its own bundled l10n/generated_date_localizations.dart. Your DatePicker keeps the SDK's CLDR vintage regardless of the override. |
Your own DateFormat |
After initializeDateFormatting(), your formatters use the overridden intl's CLDR data. You now run two CLDR vintages in one app — month abbreviations, currency symbols (0.20.3 changed Turkish Lira and Ghanaian Cedi handling) and locale-tag parsing can differ between a Material widget and a label you formatted yourself. |
NumberFormat symbols |
Same split: SDK widgets use SDK-bundled number symbols; your code uses the overridden package's. Grouping/decimal separators for rarely-tested locales are where this shows up first. |
| Compile errors in the SDK | The SDK's generated file constructs intl.DateSymbols(...) from package:intl/date_symbols.dart. If a future intl changes that constructor, the errors point inside your Flutter installation, not your code. |
| Publishing a package | dependency_overrides only apply in the root package. Consumers of your package never inherit them, so this can never be a library-level fix. |
| CI reproducibility | pub get prints ! intl 0.20.3 (overridden). Nothing else warns you again, and a later Flutter bump silently changes what the override resolves against. |
If you take this path, treat it as temporary: add a comment with the ticket to remove it, and cover formatting with tests.
Verify before you ship
flutter pub get
flutter pub deps -s list | grep -i intl # confirm the resolved version
Then smoke-test formatting in a locale you actually ship, not just en_US:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting('fr_FR', null);
debugPrint(DateFormat.yMMMMd('fr_FR').format(DateTime(2026, 3, 14)));
debugPrint(NumberFormat.currency(locale: 'tr_TR').format(1234.5));
debugPrint(NumberFormat.decimalPattern('de_DE').format(1234567.89));
}
If you forget initializeDateFormatting for a non-default locale, DateFormat throws a LocaleDataException at runtime — a failure that a version conflict often hides until after you've "fixed" the pubspec.
Keep the translation layer out of the blast radius
Dependency pins move; your app_<locale>.arb files shouldn't have to. Keeping translations clean and validated means an SDK upgrade is a build concern only — not a re-translation project. FlutterLocalisation gives you an ARB editor for app_en.arb, app_fr.arb and the rest, translation management across every locale you ship, and ICU plural-syntax validation that flags a locale missing a plural category its language genuinely needs — a dropped few or many for Arabic, Polish or Russian, exactly the kind of bug a formatting change makes visible.
More Flutter i18n walkthroughs are on the FlutterLocalisation blog, and plans are on the pricing page.
Try FlutterLocalisation free — import your ARB files and see the plural gaps in minutes.