Kate Lovett 9d96df2364
Modernize framework lints (#179089)
WIP

Commits separated as follows:
- Update lints in analysis_options files
- Run `dart fix --apply`
- Clean up leftover analysis issues 
- Run `dart format .` in the right places.

Local analysis and testing passes. Checking CI now.

Part of https://github.com/flutter/flutter/issues/178827
- Adoption of flutter_lints in examples/api coming in a separate change
(cc @loic-sharma)

## Pre-launch Checklist

- [ ] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [ ] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [ ] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [ ] I signed the [CLA].
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [ ] I updated/added relevant documentation (doc comments with `///`).
- [ ] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
2025-11-26 01:10:39 +00:00

1421 lines
50 KiB
Dart

// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
class TestLocalizations {
TestLocalizations(this.locale, this.prefix);
final Locale locale;
final String? prefix;
static Future<TestLocalizations> loadSync(Locale locale, String? prefix) {
return SynchronousFuture<TestLocalizations>(TestLocalizations(locale, prefix));
}
static Future<TestLocalizations> loadAsync(Locale locale, String? prefix) {
return Future<TestLocalizations>.delayed(
const Duration(milliseconds: 100),
() => TestLocalizations(locale, prefix),
);
}
static TestLocalizations of(BuildContext context) {
return Localizations.of<TestLocalizations>(context, TestLocalizations)!;
}
String get message => '${prefix ?? ""}$locale';
}
class SyncTestLocalizationsDelegate extends LocalizationsDelegate<TestLocalizations> {
SyncTestLocalizationsDelegate([this.prefix]);
final String? prefix; // Changing this value triggers a rebuild
final List<bool> shouldReloadValues = <bool>[];
@override
bool isSupported(Locale locale) => true;
@override
Future<TestLocalizations> load(Locale locale) => TestLocalizations.loadSync(locale, prefix);
@override
bool shouldReload(SyncTestLocalizationsDelegate old) {
shouldReloadValues.add(prefix != old.prefix);
return prefix != old.prefix;
}
@override
String toString() => '${objectRuntimeType(this, 'SyncTestLocalizationsDelegate')}($prefix)';
}
class AsyncTestLocalizationsDelegate extends LocalizationsDelegate<TestLocalizations> {
AsyncTestLocalizationsDelegate([this.prefix]);
final String? prefix; // Changing this value triggers a rebuild
final List<bool> shouldReloadValues = <bool>[];
@override
bool isSupported(Locale locale) => true;
@override
Future<TestLocalizations> load(Locale locale) => TestLocalizations.loadAsync(locale, prefix);
@override
bool shouldReload(AsyncTestLocalizationsDelegate old) {
shouldReloadValues.add(prefix != old.prefix);
return prefix != old.prefix;
}
@override
String toString() => '${objectRuntimeType(this, 'AsyncTestLocalizationsDelegate')}($prefix)';
}
class MoreLocalizations {
MoreLocalizations(this.locale);
final Locale locale;
static Future<MoreLocalizations> loadSync(Locale locale) {
return SynchronousFuture<MoreLocalizations>(MoreLocalizations(locale));
}
static Future<MoreLocalizations> loadAsync(Locale locale) {
return Future<MoreLocalizations>.delayed(
const Duration(milliseconds: 100),
() => MoreLocalizations(locale),
);
}
static MoreLocalizations of(BuildContext context) {
return Localizations.of<MoreLocalizations>(context, MoreLocalizations)!;
}
String get message => '$locale';
}
class SyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizations> {
@override
Future<MoreLocalizations> load(Locale locale) => MoreLocalizations.loadSync(locale);
@override
bool isSupported(Locale locale) => true;
@override
bool shouldReload(SyncMoreLocalizationsDelegate old) => false;
}
class AsyncMoreLocalizationsDelegate extends LocalizationsDelegate<MoreLocalizations> {
@override
Future<MoreLocalizations> load(Locale locale) => MoreLocalizations.loadAsync(locale);
@override
bool isSupported(Locale locale) => true;
@override
bool shouldReload(AsyncMoreLocalizationsDelegate old) => false;
}
class OnlyRTLDefaultWidgetsLocalizations extends DefaultWidgetsLocalizations {
@override
TextDirection get textDirection => TextDirection.rtl;
}
class OnlyRTLDefaultWidgetsLocalizationsDelegate
extends LocalizationsDelegate<WidgetsLocalizations> {
const OnlyRTLDefaultWidgetsLocalizationsDelegate();
@override
bool isSupported(Locale locale) => true;
@override
Future<WidgetsLocalizations> load(Locale locale) {
return SynchronousFuture<WidgetsLocalizations>(OnlyRTLDefaultWidgetsLocalizations());
}
@override
bool shouldReload(OnlyRTLDefaultWidgetsLocalizationsDelegate old) => false;
}
Widget buildFrame({
Locale? locale,
Iterable<LocalizationsDelegate<dynamic>>? delegates,
required WidgetBuilder buildContent,
LocaleResolutionCallback? localeResolutionCallback,
List<Locale> supportedLocales = const <Locale>[Locale('en', 'US'), Locale('en', 'GB')],
}) {
return WidgetsApp(
color: const Color(0xFFFFFFFF),
locale: locale,
localizationsDelegates: delegates,
localeResolutionCallback: localeResolutionCallback,
supportedLocales: supportedLocales,
onGenerateRoute: (RouteSettings settings) {
return PageRouteBuilder<void>(
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> _) {
return buildContent(context);
},
);
},
);
}
class SyncLoadTest extends StatefulWidget {
const SyncLoadTest({super.key});
@override
SyncLoadTestState createState() => SyncLoadTestState();
}
class SyncLoadTestState extends State<SyncLoadTest> {
@override
Widget build(BuildContext context) {
return Text(TestLocalizations.of(context).message, textDirection: TextDirection.rtl);
}
}
void main() {
testWidgets('Localizations.localeFor in a WidgetsApp with system locale', (
WidgetTester tester,
) async {
late BuildContext pageContext;
await tester.pumpWidget(
buildFrame(
buildContent: (BuildContext context) {
pageContext = context;
return const Text('Hello World', textDirection: TextDirection.ltr);
},
),
);
await tester.binding.setLocale('en', 'GB');
await tester.pump();
expect(Localizations.localeOf(pageContext), const Locale('en', 'GB'));
await tester.binding.setLocale('en', 'US');
await tester.pump();
expect(Localizations.localeOf(pageContext), const Locale('en', 'US'));
});
testWidgets('Localizations.localeFor in a WidgetsApp with an explicit locale', (
WidgetTester tester,
) async {
const locale = Locale('en', 'US');
late BuildContext pageContext;
await tester.pumpWidget(
buildFrame(
locale: locale,
buildContent: (BuildContext context) {
pageContext = context;
return const Text('Hello World');
},
),
);
expect(Localizations.localeOf(pageContext), locale);
await tester.binding.setLocale('en', 'GB');
await tester.pump();
// The WidgetApp's explicit locale overrides the system's locale.
expect(Localizations.localeOf(pageContext), locale);
});
testWidgets('Synchronously loaded localizations in a WidgetsApp', (WidgetTester tester) async {
final delegates = <LocalizationsDelegate<dynamic>>[
SyncTestLocalizationsDelegate(),
DefaultWidgetsLocalizations.delegate,
];
Future<void> pumpTest(Locale locale) async {
await tester.pumpWidget(
Localizations(locale: locale, delegates: delegates, child: const SyncLoadTest()),
);
}
await pumpTest(const Locale('en', 'US'));
expect(find.text('en_US'), findsOneWidget);
await pumpTest(const Locale('en', 'GB'));
await tester.pump();
expect(find.text('en_GB'), findsOneWidget);
await pumpTest(const Locale('en', 'US'));
await tester.pump();
expect(find.text('en_US'), findsOneWidget);
});
testWidgets('Asynchronously loaded localizations in a WidgetsApp', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[AsyncTestLocalizationsDelegate()],
buildContent: (BuildContext context) {
return Text(TestLocalizations.of(context).message);
},
),
);
await tester.pump(
const Duration(milliseconds: 50),
); // TestLocalizations.loadAsync() takes 100ms
expect(find.text('en_US'), findsNothing); // TestLocalizations hasn't been loaded yet
await tester.pump(const Duration(milliseconds: 50)); // TestLocalizations.loadAsync() completes
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget); // default test locale is US english
await tester.binding.setLocale('en', 'GB');
await tester.pump(const Duration(milliseconds: 100));
await tester.pumpAndSettle();
expect(find.text('en_GB'), findsOneWidget);
await tester.binding.setLocale('en', 'US');
await tester.pump(const Duration(milliseconds: 50));
// TestLocalizations.loadAsync() hasn't completed yet so the old text
// localization is still displayed
expect(find.text('en_GB'), findsOneWidget);
await tester.pump(const Duration(milliseconds: 50)); // finish the async load
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
});
testWidgets('Localizations with multiple sync delegates', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
SyncTestLocalizationsDelegate(),
SyncMoreLocalizationsDelegate(),
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
// All localizations were loaded synchronously
expect(find.text('A: en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
});
testWidgets('Localizations with multiple delegates', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
SyncTestLocalizationsDelegate(),
AsyncMoreLocalizationsDelegate(), // No resources until this completes
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
await tester.pump(const Duration(milliseconds: 50));
expect(find.text('A: en_US'), findsNothing); // MoreLocalizations.load() hasn't completed yet
expect(find.text('B: en_US'), findsNothing);
await tester.pump(const Duration(milliseconds: 50));
await tester.pumpAndSettle();
expect(find.text('A: en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
});
testWidgets('Multiple Localizations', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[SyncTestLocalizationsDelegate()],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Localizations(
locale: const Locale('en', 'GB'),
delegates: <LocalizationsDelegate<dynamic>>[
SyncTestLocalizationsDelegate(),
DefaultWidgetsLocalizations.delegate,
],
// Create a new context within the en_GB Localization
child: Builder(
builder: (BuildContext context) {
return Text('B: ${TestLocalizations.of(context).message}');
},
),
),
],
);
},
),
);
expect(find.text('A: en_US'), findsOneWidget);
expect(find.text('B: en_GB'), findsOneWidget);
});
// If both the locale and the length and type of a Localizations delegate list
// stays the same BUT one of its delegate.shouldReload() methods returns true,
// then the dependent widgets should rebuild.
testWidgets('Localizations sync delegate shouldReload returns true', (WidgetTester tester) async {
final originalDelegate = SyncTestLocalizationsDelegate();
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
originalDelegate,
SyncMoreLocalizationsDelegate(),
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
await tester.pumpAndSettle();
expect(find.text('A: en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
expect(originalDelegate.shouldReloadValues, <bool>[]);
final modifiedDelegate = SyncTestLocalizationsDelegate('---');
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
modifiedDelegate,
SyncMoreLocalizationsDelegate(),
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
await tester.pumpAndSettle();
expect(find.text('A: ---en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
expect(modifiedDelegate.shouldReloadValues, <bool>[true]);
expect(originalDelegate.shouldReloadValues, <bool>[]);
});
testWidgets('Localizations async delegate shouldReload returns true', (
WidgetTester tester,
) async {
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
AsyncTestLocalizationsDelegate(),
AsyncMoreLocalizationsDelegate(),
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
await tester.pumpAndSettle();
expect(find.text('A: en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
final modifiedDelegate = AsyncTestLocalizationsDelegate('---');
await tester.pumpWidget(
buildFrame(
delegates: <LocalizationsDelegate<dynamic>>[
modifiedDelegate,
AsyncMoreLocalizationsDelegate(),
],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Column(
children: <Widget>[
Text('A: ${TestLocalizations.of(context).message}'),
Text('B: ${MoreLocalizations.of(context).message}'),
],
);
},
),
);
await tester.pumpAndSettle();
expect(find.text('A: ---en_US'), findsOneWidget);
expect(find.text('B: en_US'), findsOneWidget);
expect(modifiedDelegate.shouldReloadValues, <bool>[true]);
});
testWidgets('Directionality tracks system locale', (WidgetTester tester) async {
late BuildContext pageContext;
await tester.pumpWidget(
buildFrame(
delegates: const <LocalizationsDelegate<dynamic>>[GlobalWidgetsLocalizations.delegate],
supportedLocales: const <Locale>[Locale('en', 'GB'), Locale('ar', 'EG')],
buildContent: (BuildContext context) {
pageContext = context;
return const Text('Hello World');
},
),
);
await tester.binding.setLocale('en', 'GB');
await tester.pump();
expect(Directionality.of(pageContext), TextDirection.ltr);
await tester.binding.setLocale('ar', 'EG');
await tester.pump();
expect(Directionality.of(pageContext), TextDirection.rtl);
});
testWidgets('localeResolutionCallback override', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
localeResolutionCallback: (Locale? newLocale, Iterable<Locale> supportedLocales) {
return const Locale('foo', 'BAR');
},
buildContent: (BuildContext context) {
return Text(Localizations.localeOf(context).toString());
},
),
);
await tester.pumpAndSettle();
expect(find.text('foo_BAR'), findsOneWidget);
await tester.binding.setLocale('en', 'GB');
await tester.pumpAndSettle();
expect(find.text('foo_BAR'), findsOneWidget);
});
testWidgets('supportedLocales and defaultLocaleChangeHandler', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('zh', 'CN'),
Locale('en', 'GB'),
Locale('en', 'CA'),
],
buildContent: (BuildContext context) {
return Text(Localizations.localeOf(context).toString());
},
),
);
await tester.pumpAndSettle();
expect(find.text('en_GB'), findsOneWidget);
// defaultLocaleChangedHandler prefers exact supported locale match
await tester.binding.setLocale('en', 'CA');
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
// defaultLocaleChangedHandler chooses 1st matching supported locale.languageCode
await tester.binding.setLocale('en', 'US');
await tester.pumpAndSettle();
expect(find.text('en_GB'), findsOneWidget);
// defaultLocaleChangedHandler: no matching supported locale, so use the 1st one
await tester.binding.setLocale('da', 'DA');
await tester.pumpAndSettle();
expect(find.text('zh_CN'), findsOneWidget);
});
testWidgets("Localizations.override widget tracks parent's locale and delegates", (
WidgetTester tester,
) async {
await tester.pumpWidget(
buildFrame(
// Accept whatever locale we're given
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) => locale,
delegates: const <LocalizationsDelegate<dynamic>>[GlobalWidgetsLocalizations.delegate],
buildContent: (BuildContext context) {
return Localizations.override(
context: context,
child: Builder(
builder: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
return Text('$locale $direction');
},
),
);
},
),
);
// Initial WidgetTester locale is `en_US`.
await tester.pumpAndSettle();
expect(find.text('en_US TextDirection.ltr'), findsOneWidget);
await tester.binding.setLocale('en', 'CA');
await tester.pumpAndSettle();
expect(find.text('en_CA TextDirection.ltr'), findsOneWidget);
await tester.binding.setLocale('ar', 'EG');
await tester.pumpAndSettle();
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('da', 'DA');
await tester.pumpAndSettle();
expect(find.text('da_DA TextDirection.ltr'), findsOneWidget);
});
testWidgets("Localizations.override widget overrides parent's DefaultWidgetLocalizations", (
WidgetTester tester,
) async {
await tester.pumpWidget(
buildFrame(
// Accept whatever locale we're given
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) => locale,
buildContent: (BuildContext context) {
return Localizations.override(
context: context,
delegates: const <OnlyRTLDefaultWidgetsLocalizationsDelegate>[
// Override: no matter what the locale, textDirection is always RTL.
OnlyRTLDefaultWidgetsLocalizationsDelegate(),
],
child: Builder(
builder: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
return Text('$locale $direction');
},
),
);
},
),
);
// Initial WidgetTester locale is `en_US`.
await tester.pumpAndSettle();
expect(find.text('en_US TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('en', 'CA');
await tester.pumpAndSettle();
expect(find.text('en_CA TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('ar', 'EG');
await tester.pumpAndSettle();
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('da', 'DA');
await tester.pumpAndSettle();
expect(find.text('da_DA TextDirection.rtl'), findsOneWidget);
});
testWidgets('WidgetsApp overrides DefaultWidgetLocalizations', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
// Accept whatever locale we're given
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) => locale,
delegates: <OnlyRTLDefaultWidgetsLocalizationsDelegate>[
const OnlyRTLDefaultWidgetsLocalizationsDelegate(),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
final TextDirection direction = WidgetsLocalizations.of(context).textDirection;
return Text('$locale $direction');
},
),
);
// Initial WidgetTester locale is `en_US`.
await tester.pumpAndSettle();
expect(find.text('en_US TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('en', 'CA');
await tester.pumpAndSettle();
expect(find.text('en_CA TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('ar', 'EG');
await tester.pumpAndSettle();
expect(find.text('ar_EG TextDirection.rtl'), findsOneWidget);
await tester.binding.setLocale('da', 'DA');
await tester.pumpAndSettle();
expect(find.text('da_DA TextDirection.rtl'), findsOneWidget);
});
// We provide <Locale>[Locale('en', 'US'), Locale('zh', 'CN')] as ui.window.locales
// for flutter tester so that the behavior of tests match that of production
// environments. Here, we test the default locales.
testWidgets('WidgetsApp DefaultWidgetLocalizations', (WidgetTester tester) async {
await tester.pumpAndSettle();
await tester.pumpWidget(
buildFrame(
// Accept whatever locale we're given
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) => locale,
delegates: <OnlyRTLDefaultWidgetsLocalizationsDelegate>[
const OnlyRTLDefaultWidgetsLocalizationsDelegate(),
],
buildContent: (BuildContext context) {
final Locale locale1 = WidgetsBinding.instance.platformDispatcher.locales.first;
final Locale locale2 = WidgetsBinding.instance.platformDispatcher.locales[1];
return Text('$locale1 $locale2');
},
),
);
// Initial WidgetTester default locales is `en_US` and `zh_CN`.
await tester.pumpAndSettle();
expect(find.text('en_US zh_CN'), findsOneWidget);
});
testWidgets('WidgetsApp.locale is resolved against supportedLocales', (
WidgetTester tester,
) async {
// app locale matches a supportedLocale
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[Locale('zh', 'CN'), Locale('en', 'US')],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Text(Localizations.localeOf(context).toString());
},
),
);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
// app locale matches a supportedLocale's language
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[Locale('zh', 'CN'), Locale('en', 'GB')],
locale: const Locale('en', 'US'),
buildContent: (BuildContext context) {
return Text(Localizations.localeOf(context).toString());
},
),
);
await tester.pumpAndSettle();
expect(find.text('en_GB'), findsOneWidget);
// app locale matches no supportedLocale
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[Locale('zh', 'CN'), Locale('en', 'US')],
locale: const Locale('ab', 'CD'),
buildContent: (BuildContext context) {
return Text(Localizations.localeOf(context).toString());
},
),
);
await tester.pumpAndSettle();
expect(find.text('zh_CN'), findsOneWidget);
});
// Example from http://unicode.org/reports/tr35/#LanguageMatching
testWidgets('WidgetsApp Unicode tr35 1', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[Locale('de'), Locale('fr'), Locale('ja')],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'de', countryCode: 'AT'),
Locale.fromSubtags(languageCode: 'fr'),
]);
await tester.pumpAndSettle();
expect(find.text('de'), findsOneWidget);
});
// Examples from http://unicode.org/reports/tr35/#LanguageMatching
testWidgets('WidgetsApp Unicode tr35 2', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[Locale('ja', 'JP'), Locale('de'), Locale('zh', 'TW')],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[Locale.fromSubtags(languageCode: 'zh')]);
await tester.pumpAndSettle();
expect(find.text('zh_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'de'),
Locale.fromSubtags(languageCode: 'fr'),
Locale.fromSubtags(languageCode: 'de', countryCode: 'SW'),
Locale.fromSubtags(languageCode: 'it'),
]);
await tester.pumpAndSettle();
expect(find.text('de'), findsOneWidget);
});
testWidgets('WidgetsApp EdgeCase Chinese', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale.fromSubtags(languageCode: 'zh'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'de'),
Locale.fromSubtags(languageCode: 'fr'),
Locale.fromSubtags(languageCode: 'de', countryCode: 'SW'),
Locale.fromSubtags(languageCode: 'zh'),
]);
await tester.pumpAndSettle();
expect(find.text('zh'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('zh'), findsOneWidget);
// This behavior is up to the implementer to decide if a perfect scriptCode match
// is better than a countryCode match.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
// languageCode only match is not enough to prevent resolving a perfect match
// further down the preferredLocales list.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'JP'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
// When no language match, we try for country only, since it is likely users are
// at least familiar with their country's language. This is a possible case only
// on iOS, where countryCode can be selected independently from language and script.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', scriptCode: 'Hans', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
});
// Same as 'WidgetsApp EdgeCase Chinese' test except the supportedLocales order is
// reversed.
testWidgets('WidgetsApp EdgeCase ReverseChinese', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'HK'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'de'),
Locale.fromSubtags(languageCode: 'fr'),
Locale.fromSubtags(languageCode: 'de', countryCode: 'SW'),
Locale.fromSubtags(languageCode: 'zh'),
]);
await tester.pumpAndSettle();
expect(find.text('zh'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
// This behavior is up to the implementer to decide if a perfect scriptCode match
// is better than a countryCode match.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
// languageCode only match is not enough to prevent resolving a perfect match
// further down the preferredLocales list.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'JP'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'zh', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hans_CN'), findsOneWidget);
// When no language match, we try for country only, since it is likely users are
// at least familiar with their country's language. This is a possible case only
// on iOS, where countryCode can be selected independently from language and script.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', scriptCode: 'Hans', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'TW'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_TW'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'HK'),
]);
await tester.pumpAndSettle();
expect(find.text('zh_Hant_HK'), findsOneWidget);
});
// Examples from https://developer.android.com/guide/topics/resources/multilingual-support
testWidgets('WidgetsApp Android', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('en'),
Locale('de', 'DE'),
Locale('es', 'ES'),
Locale('fr', 'FR'),
Locale('it', 'IT'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'fr', countryCode: 'CH'),
]);
await tester.pumpAndSettle();
expect(find.text('fr_FR'), findsOneWidget);
});
// Examples from https://developer.android.com/guide/topics/resources/multilingual-support
testWidgets('WidgetsApp Android', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('en'),
Locale('de', 'DE'),
Locale('es', 'ES'),
Locale('it', 'IT'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'fr', countryCode: 'CH'),
Locale.fromSubtags(languageCode: 'it', countryCode: 'CH'),
]);
await tester.pumpAndSettle();
expect(find.text('it_IT'), findsOneWidget);
});
testWidgets('WidgetsApp Country-only fallback', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('en', 'US'),
Locale('de', 'DE'),
Locale('de', 'AU'),
Locale('de', 'LU'),
Locale('de', 'CH'),
Locale('es', 'ES'),
Locale('es', 'US'),
Locale('it', 'IT'),
Locale('zh', 'CN'),
Locale('zh', 'TW'),
Locale('fr', 'FR'),
Locale('br', 'FR'),
Locale('pt', 'BR'),
Locale('pt', 'PT'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'CH'),
]);
await tester.pumpAndSettle();
expect(find.text('de_CH'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'FR'),
]);
await tester.pumpAndSettle();
expect(find.text('fr_FR'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'es', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('es_US'), findsOneWidget);
// Strongly prefer matching first locale even if next one is perfect.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'pt'),
Locale.fromSubtags(languageCode: 'pt', countryCode: 'PT'),
]);
await tester.pumpAndSettle();
expect(find.text('pt_PT'), findsOneWidget);
// Don't country match with any other available match. This behavior is
// up for reconsideration.
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'BR'),
Locale.fromSubtags(languageCode: 'pt'),
]);
await tester.pumpAndSettle();
expect(find.text('pt_BR'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'BR'),
Locale.fromSubtags(languageCode: 'pt', countryCode: 'PT'),
]);
await tester.pumpAndSettle();
expect(find.text('pt_PT'), findsOneWidget);
});
// Simulates a Chinese-default app that supports english in Canada but not
// French. French-Canadian users should get 'en_CA' instead of Chinese.
testWidgets('WidgetsApp Multilingual country', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('zh', 'CN'),
Locale('en', 'CA'),
Locale('en', 'US'),
Locale('en', 'AU'),
Locale('de', 'DE'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'fr', countryCode: 'CA'),
Locale.fromSubtags(languageCode: 'fr'),
]);
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
});
testWidgets('WidgetsApp Common cases', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
// Decently well localized app.
supportedLocales: const <Locale>[
Locale('en', 'US'),
Locale('en', 'GB'),
Locale('en', 'AU'),
Locale('en', 'CA'),
Locale('zh', 'CN'),
Locale('zh', 'TW'),
Locale('de', 'DE'),
Locale('de', 'CH'),
Locale('es', 'MX'),
Locale('es', 'ES'),
Locale('es', 'AR'),
Locale('es', 'CO'),
Locale('ru', 'RU'),
Locale('fr', 'FR'),
Locale('fr', 'CA'),
Locale('ar', 'SA'),
Locale('ar', 'EG'),
Locale('ar', 'IQ'),
Locale('ar', 'MA'),
Locale('af'),
Locale('bg'),
Locale('nl', 'NL'),
Locale('pl'),
Locale('cs'),
Locale('fa'),
Locale('el'),
Locale('he'),
Locale('hi'),
Locale('pa'),
Locale('ta'),
Locale('id'),
Locale('it', 'IT'),
Locale('ja'),
Locale('ko'),
Locale('ms'),
Locale('mn'),
Locale('pt', 'BR'),
Locale('pt', 'PT'),
Locale('sv', 'SE'),
Locale('th'),
Locale('tr'),
Locale('vi'),
],
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[Locale.fromSubtags(languageCode: 'en')]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'CA'),
]);
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'AU'),
]);
await tester.pumpAndSettle();
expect(find.text('en_AU'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'CH'),
]);
await tester.pumpAndSettle();
expect(find.text('ar_SA'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[Locale.fromSubtags(languageCode: 'ar')]);
await tester.pumpAndSettle();
expect(find.text('ar_SA'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ar', countryCode: 'IQ'),
]);
await tester.pumpAndSettle();
expect(find.text('ar_IQ'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'es', countryCode: 'ES'),
]);
await tester.pumpAndSettle();
expect(find.text('es_ES'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[Locale.fromSubtags(languageCode: 'es')]);
await tester.pumpAndSettle();
expect(find.text('es_MX'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'pa', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('pa'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'hi', countryCode: 'IN'),
]);
await tester.pumpAndSettle();
expect(find.text('hi'), findsOneWidget);
// Multiple preferred locales:
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'NZ'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'AU'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'GB'),
Locale.fromSubtags(languageCode: 'en'),
]);
await tester.pumpAndSettle();
expect(find.text('en_AU'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'ab'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'NZ'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'AU'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'GB'),
Locale.fromSubtags(languageCode: 'en'),
]);
await tester.pumpAndSettle();
expect(find.text('en_AU'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'NZ'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'PH'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'ZA'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'CB'),
]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'CA'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'AU'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'GB'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'da'),
Locale.fromSubtags(languageCode: 'en'),
Locale.fromSubtags(languageCode: 'en', countryCode: 'CA'),
]);
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'da'),
Locale.fromSubtags(languageCode: 'fo'),
Locale.fromSubtags(languageCode: 'hr'),
]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'da'),
Locale.fromSubtags(languageCode: 'fo'),
Locale.fromSubtags(languageCode: 'hr', countryCode: 'CA'),
]);
await tester.pumpAndSettle();
expect(find.text('en_CA'), findsOneWidget);
});
testWidgets('WidgetsApp invalid preferredLocales', (WidgetTester tester) async {
await tester.pumpWidget(
buildFrame(
supportedLocales: const <Locale>[
Locale('zh', 'CN'),
Locale('en', 'CA'),
Locale('en', 'US'),
Locale('en', 'AU'),
Locale('de', 'DE'),
],
localeResolutionCallback: (Locale? locale, Iterable<Locale> supportedLocales) {
if (locale == null) {
return const Locale('und', 'US');
}
return const Locale('en', 'US');
},
buildContent: (BuildContext context) {
final Locale locale = Localizations.localeOf(context);
return Text('$locale');
},
),
);
await tester.binding.setLocales(const <Locale>[
Locale.fromSubtags(languageCode: 'en', countryCode: 'US'),
]);
await tester.pumpAndSettle();
expect(find.text('en_US'), findsOneWidget);
await tester.binding.setLocales(const <Locale>[]);
await tester.pumpAndSettle();
expect(find.text('und_US'), findsOneWidget);
});
}