Instances of `CupertinoTabBar` must have a `Localizations`parent in order to provide a localized `Semantics` hint. Trying to instantiate a `CupertinoTabBar` without localizations results in an assertion such as the following:
Original behavior
To support localized semantics information, the `CupertinoTabBar` requires localizations.
New behavior
Not stated in the source.
- Who is affected
- Applications and packages relying on the previous behaviour or signature. Code that uses CupertinoTabBar, Localizations, Semantics and related APIs.
- Practical impact
- Behaviour or API differences that can break builds or change runtime behaviour.
- What action should I take?
- Follow the official migration guide below, update affected call sites, run `dart fix --apply` where a fix is available, then run your tests.
Before and after (from the official guide)
| 1 | import 'package:cupertino_ui/cupertino_ui.dart'; | |
| 2 | ||
| 3 | void main() => runApp(Foo()); | |
| 4 | ||
| 5 | class Foo extends StatelessWidget { | |
| 6 | @override | |
| 7 | Widget build(BuildContext context) { | |
| 8 | − | return MediaQuery( |
| 9 | − | data: const MediaQueryData(), |
| 10 | − | child: CupertinoTabBar( |
| 11 | items: const <BottomNavigationBarItem>[ | |
| 12 | BottomNavigationBarItem( | |
| 13 | icon: Icon(CupertinoIcons.add_circled), | |
| 14 | label: 'Tab 1', | |
| 15 | ), | |
| 16 | BottomNavigationBarItem( | |
| 17 | icon: Icon(CupertinoIcons.add_circled_solid), | |
| 18 | label: 'Tab 2', | |
| 19 | ), | |
| 20 | ], | |
| 21 | currentIndex: 1, | |
| 22 | ), | |
| 23 | ); | |
| 24 | } | |
| 25 | } |
| 1 | import 'package:cupertino_ui/cupertino_ui.dart'; | |
| 2 | ||
| 3 | void main() => runApp(Foo()); | |
| 4 | ||
| 5 | class Foo extends StatelessWidget { | |
| 6 | @override | |
| 7 | Widget build(BuildContext context) { | |
| 8 | + | return CupertinoApp( |
| 9 | + | home: CupertinoTabBar( |
| 10 | items: const <BottomNavigationBarItem>[ | |
| 11 | BottomNavigationBarItem( | |
| 12 | icon: Icon(CupertinoIcons.add_circled), | |
| 13 | label: 'Tab 1', | |
| 14 | ), | |
| 15 | BottomNavigationBarItem( | |
| 16 | icon: Icon(CupertinoIcons.add_circled_solid), | |
| 17 | label: 'Tab 2', | |
| 18 | ), | |
| 19 | ], | |
| 20 | currentIndex: 1, | |
| 21 | ), | |
| 22 | ); | |
| 23 | } | |
| 24 | } |
Raw before / after
import 'package:cupertino_ui/cupertino_ui.dart';
void main() => runApp(Foo());
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled_solid),
label: 'Tab 2',
),
],
currentIndex: 1,
),
);
}
}import 'package:cupertino_ui/cupertino_ui.dart';
void main() => runApp(Foo());
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoApp(
home: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled_solid),
label: 'Tab 2',
),
],
currentIndex: 1,
),
);
}
}Official migration guide (excerpt, © The Flutter Authors, CC BY 3.0)
If you are seeing a 'localizations != null' assertion error,
make sure locale information is being
provided to your CupertinoTabBar.
Code before migration:
import 'package:cupertino_ui/cupertino_ui.dart';
void main() => runApp(Foo());
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled_solid),
label: 'Tab 2',
),
],
currentIndex: 1,
),
);
}
}Code after migration (Providing localizations via the CupertinoApp):
import 'package:cupertino_ui/cupertino_ui.dart';
void main() => runApp(Foo());
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoApp(
home: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled_solid),
label: 'Tab 2',
),
],
currentIndex: 1,
),
);
}
}Code after migration (Providing localizations by using
the Localizations widget):
import 'package:cupertino_ui/cupertino_ui.dart';
void main() => runApp(Foo());
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Localizations(
locale: const Locale('en', 'US'),
delegates: <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultCupertinoLocalizations.delegate,
],
child: MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.add_circled_solid),
label: 'Tab 2',
),
],
currentIndex: 1,
),
),
);
}
}AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.Listed in the official breaking-changes index (docs.flutter.dev/release/breaking-changes).
An official announcement or migration guide documents an intended behaviour or API change. A verified source change (git diff) shows code changed but does not by itself prove a public API is breaking. AI interpretations are labelled and never used to classify a change.
- Official migration guide
- Localizations
- Semantics
- PR 55336: Adding tabSemanticsLabel to CupertinoLocalizations
- PR 56582: Update Tab semantics in Cupertino to be the same as Material
- Source commit
- b3045439b7c0
- Source last updated
- Oct 21, 2026
- Last synchronized
- Oct 21, 2026
- Publication status
- published
Publication history
- Oct 21, 2026, 8:31 PM: new → published (ingestion) — Backed by an official breaking-change/migration guide.
AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.