Open julianniedermaier opened 1 year ago
Hi @julianniedermaier In order to address the issue properly, please provide a completed and minimal reproducible code sample that doesn’t include 3rd party plugins or complex production code so that we may verify this.
This may be related to https://github.com/flutter/flutter/issues/84053.
Here is a minimal reproducible code sample with l10n and a supported locale list of ['en', 'ar', 'de']:
import 'package:advanced_internationalization/l10n/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
import 'package:flutter_test/flutter_test.dart';
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
supportedLocales: AppLocalizations.supportedLocales,
localeListResolutionCallback: basicLocaleListResolution,
localizationsDelegates: [
...AppLocalizations.localizationsDelegates,
LocaleNamesLocalizationsDelegate(),
],
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}
void main() {
group('Locale Integration Tests', () {
final supportedLocale = AppLocalizations.supportedLocales[1]; // 'ar'
final supportedLocaleAlternative = AppLocalizations.supportedLocales[2]; // 'de'
final supportedLocalesList = [supportedLocale, supportedLocaleAlternative];
testWidgets('Start in first device locale (supported)',
(WidgetTester tester) async {
tester.platformDispatcher.localesTestValue = supportedLocalesList;
expect(tester.platformDispatcher.locales, supportedLocalesList);
await tester.pumpWidget(const App());
await tester.pumpAndSettle();
debugDumpApp();
final context = tester.element(find.byType(HomePage));
expect(Localizations.localeOf(context), supportedLocalesList.first);
});
});
}
Thank you for requesting this! Trimming it down more and more I figured out the addition of the LocaleNamesLocalizationsDelegate() (not necessary in this code sample, but left in for reproduction) is causing the problem. Now this is a package I only recently started using, but it seems to be built rather simple and I don't see how it causes the MaterialApp build to fail. See the simple code file here: https://github.com/guidezpl/flutter-localized-locales/tree/master/lib Has something changed recently in how delegates are being handled?
Thanks for the update.
I checked this with a simpler sample at Flutter-demo-l10n/134999 but can't reproduce the issue, the test can run successfully. You can try it and update the code if it's necessary to replicate this issue.
Regarding delegates
issue, I'm not sure about this. You can check Flutter release notes and find related changes if any.
Regarding sample code using flutter_localized_locales
, I created below sample but got another error: Bad state: No element
. Please also try and modify it to reproduce the initial issue. If the issue is only reproducible with flutter_localized_locales
package, please file an issue at its repository for better support there.
Thanks for testing it. I might have communicated this badly. The Bad state: No element
error is the one I am getting as well.
The interesting thing is how this error is coming about. I have attached a section of my terminal log below for both the failing and the passing test.
This is how I understand it:
The Bad state: No element
is triggered by line 29 in the test code - final context = tester.element(find.byType(HomePage));
- which is failing to find the HomePage
.
The reason it is not finding the HomePage is because the MaterialApp never navigates to the HomePage. As you can see in the debugDumpApp()
report (which is requested before faulty line 29 in the test code), the MaterialApp stops its process after ...->Semantics->Localizations->SizedBox. (see below)
If you run your code with 'es' instead of 'ar' you will see that the debugDumpApp() report shows ...->Semantics->Localizations->Semantics->...->HomePage->...
So, using your code (with the flutter_localized_locales
) and only swapping the primary locale from 'ar' to 'es' results in a very different debugDumpApp()
report. Using 'ar' the MaterialApp never navigates to the HomePage, which means find.byType(HomePage)
never finds the HomePage, which in turn triggers the Bad state: No element
error.
Please correct me if I misunderstood this. But if this is correct I am confused how the combination of the delegate and 'ar' is causing such an issue.
Terminal - Locale('ar') - Failing:
...
└_FocusInheritedScope
└Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#557f6)
└Localizations(locale: ar, delegates: [_AppLocalizationsDelegate[AppLocalizations], GlobalMaterialLocalizations.delegate(79 locales), GlobalCupertinoLocalizations.delegate(78 locales), GlobalWidgetsLocalizations.delegate(79 locales), LocaleNamesLocalizationsDelegate[LocaleNames], DefaultMaterialLocalizations.delegate(en_US), DefaultCupertinoLocalizations.delegate(en_US), DefaultWidgetsLocalizations.delegate(en_US)], state: _LocalizationsState#d3f34)
└SizedBox.shrink(renderObject: RenderConstrainedBox#87ff8)
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following StateError was thrown running a test:
Bad state: No element
#2 main.<anonymous closure>.<anonymous closure> (file:///C:/Users/Julian/Downloads/test_134999/test_134999/test/widget_test.dart:29:30)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
The test description was:
Start in first device locale (supported)
...
Terminal - Locale('es') - passing
...
└Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#62f3a)
└Localizations(locale: es, delegates: [_AppLocalizationsDelegate[AppLocalizations], GlobalMaterialLocalizations.delegate(79 locales), GlobalCupertinoLocalizations.delegate(78 locales), GlobalWidgetsLocalizations.delegate(79 locales), LocaleNamesLocalizationsDelegate[LocaleNames], DefaultMaterialLocalizations.delegate(en_US), DefaultCupertinoLocalizations.delegate(en_US), DefaultWidgetsLocalizations.delegate(en_US)], state: _LocalizationsState#dd80e)
└Semantics(container: false, properties: SemanticsProperties, tooltip: null, textDirection: ltr, renderObject: RenderSemanticsAnnotations#2f4b3)
...
└Builder
└Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#bf890)
└HomePage
└Placeholder
└LimitedBox(maxWidth: 400.0, maxHeight: 400.0, renderObject: RenderLimitedBox#2ff29)
└CustomPaint(renderObject: RenderCustomPaint#e8fd1)
00:14 +1: All tests passed!
I just figured out that if I comment out LocaleNamesLocalizationsDelegate
from localizationsDelegates
, the test is successful (you can try this as well). Since it comes from flutter-localized-locales
package, perhaps Pierre-Louis might give us more insights if this is a Flutter framework or package issue.
cc @guidezpl for thoughts.
I haven't touched that package substantially in a number of years, and nothing you're doing seems obviously wrong. Sorry I can't be of more help. I'd have a look in https://github.com/guidezpl/flutter-localized-locales/blob/main/lib/flutter_localized_locales.dart#L34-L85 to see where a failure (in loading JSON, for example) might be ocurring
Thanks a lot to both of you! Pierre-Louis it is a fantastic package!
I implemented flutter_localized_locales
the same way as it is done in the Flutter Gallery App and so didn't assume the problem could lie in the code there.
Debugging the file I think the Bad state: no element
error might not actually be triggered by line 29 of the test code, but when trying to resolve the future final data = Map<String, String>.from(await _loadJSON('data/$localeToLoad.json') as Map<dynamic, dynamic>);
in this line in flutter_localized_locals.
To narrow it down, I have specifically tried final testData = await _loadJSON('data/$localeToLoad.json');
and final testData = _loadJSON('data/$localeToLoad.json');
, where the former fails and the latter assigns an Instance of 'Future<dynamic>'
to testData
.
I have then tried to check if I can access the raw data using rawDataStr = await rootBundle.loadString('data/$localeToLoad.json');
, but this code fails with Unable to load asset: "data/es.json".
or Unable to load asset: "data/ar.json".
regardless of locale. That is one side confusing, but then maybe also means it isn't the problem, since Locale('es') doesn't cause any issues in the normal code.
I have to admit this is now going beyond my abilities in programming. For further assistance I would be immensely thankful!
I think I just figured out the FakeAsync
the test framework runs in is causing (at least part of) the issue. It seems some of the non-latin scripts need a tiny bit too long to jsonDecode causing the test to complete before the data has been decoded. Running the test with tester.runAsync(() async {...
allows Locale('ar') data to be loaded from the ar.json file. Unfortunately, the overall test still fails. Could this have something to do with where the @overwrite load
function is called in the LocalizationsDelegate?
Maybe related to this: https://stackoverflow.com/questions/54021267/test-breaks-when-using-future-delayed
Maybe related to: #22193
Labeling the issue to have more opinions from others. Reproduced this issue with sample code: flutter_localized_locales sample project on the latest Flutter stable and master channels.
Labeling this P3 not because it's unimportant; only because there's not a simple test case that isolates the problem to Flutter's i18n tools - https://docs.flutter.dev/ui/accessibility-and-localization/internationalization
In my original issue report I mentioned a second problem, which seems to be much more severe and is most likely caused by the same issue.
Please find a simple sample code here: https://github.com/julianniedermaier/locale_test_issue/tree/main
Steps to reproduce: 1) Open project in IDE 2) Run pub get 3) Connect physical android device to IDE 4) Run project - Important: Do not interact with the app once it has been built on your android device 5) Stop the execution 6) Unplug the android device from your computer 7) Press the button on the app (this should be the first time you are interacting with the app)
Expected outcome: The app changes language from english to arabic
Actual outcome: The app stays in english
Some explanation on what is happening:
I have again added flutter_localized_locales
to the project, which loads a json file when a language is changed. This file is larger on unicode files than on latin-script files (i.e. the 'ar' file is significantly larger than the 'en' file). Running the app the first time in a new locale on the device disconnected from a computer results in the material app building before the json file has been fully loaded. The material app therefore stays in the current locale and does not change.
The reasons for why this works succesfully when connected to the computer and does not on a disconnected computer I don't know. However, if this is reproducible it means the issue raised in this issue report is not limited to locale testing, but affects normal app development and USAGE.
Just adding to my previous comment: The issue is (for me) also repoducible in the flutter gallery app. Download the current gallery app code from github. Run it from the IDE onto my android phone. Disconnect the phone, change to 'ar'. The test direction changes (as the gallery handles this separately), but the language does not
Hi @julianniedermaier, I encountered a condition where long load times for the localized message delegate caused the messages to not display. This is due to a bug in the i18n library which happens if you request any message before the delegate has loaded the locale for that message. See: https://github.com/dart-lang/i18n/pull/783
Is there an existing issue for this?
Steps to reproduce
Unsure if this is a bug or I have just managed to break my project.
I am building a project for internationalization to be used in other projects later on. The project is using l10n and BLoC for state management. The supported languages are 'en' (default), 'ar', 'de', 'es', etc. The project works without any issues when run on a connected android device. However, automated tests fail with certain locales ('ar', 'be', 'fa', etc.) as the MaterialApp tree isn't building fully. After encountering the error and being unable to solve it I, to ensure I haven't introduced errors during the design, have started again from a blank canvas only copy-pasting my code snippets, but setting up the l10n, pubspec, etc. from scratch. The error remains that non-latin scripts fail in tests. Additionally, after unplugging the android device (after successfully running and stopping the app) any non-latin languages that have not been triggered while the phone was connected also fail to build. The app does not default to 'en', but ignores all locale changes.
I'm unsure if I am missing some initialization - WidgetsFlutterBinding.ensureInitialized(); is initialized in main() - or some boolean I need to trigger.
If this is not a bug, please close and my apologies.
Expected results
Material App building fully (using Locale('ar') and starting HomePage. Test to be able to find the HomePage element - final context = tester.element(find.byType(HomePage)); - this works successfully on locales like 'en', 'de', 'es' ...
Actual results
Material App stopping build halfway through (not building title, directionality, debugBanner, home etc.). Test failing as it cannot find the HomePage
Code sample
Code sample
```dart import 'package:advanced_internationalization/home/home.dart'; import 'package:advanced_internationalization/l10n/l10n.dart'; import 'package:advanced_internationalization/settings/settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localized_locales/flutter_localized_locales.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:settings_repository/settings_repository.dart'; /// The top-level widget representing the entire application. class App extends StatelessWidget { /// Creates an instance of the [App] widget and initializes the required /// BLoc providers. const App({ required this.settingsRepository, super.key, }); /// The [settingsRepository] is responsible for managing the application's /// settings, such as locale and theme preferences. final SettingsRepository settingsRepository; @override Widget build(BuildContext context) { return RepositoryProviderScreenshots or Video
Screenshots / Video demonstration
[Upload media here]Logs
Logs
```console AutomatedTestWidgetsFlutterBinding - DEBUG MODE [root](renderObject: RenderView#44f06) └View-[GlobalObjectKey TestFlutterView#9c6fe] └_ViewScope └_MediaQueryFromView(state: _MediaQueryFromViewState#044ed) └MediaQuery(MediaQueryData(size: Size(800.0, 600.0), devicePixelRatio: 3.0, textScaleFactor: 1.0, platformBrightness: Brightness.light, padding: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, systemGestureInsets: EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast: false, disableAnimations: false, invertColors: false, boldText: false, navigationMode: traditional, gestureSettings: DeviceGestureSettings(touchSlop: null), displayFeatures: [])) └App └RepositoryProviderFlutter Doctor output
Doctor output
```console Doctor summary (to see all details, run flutter doctor -v): [√] Flutter (Channel stable, 3.13.4, on Microsoft Windows [Version 10.0.19045.3324], locale en-GB) [√] Windows Version (Installed version of Windows is version 10 or higher) [√] Android toolchain - develop for Android devices (Android SDK version 33.0.0) [√] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.3.3) [√] Android Studio (version 2022.3) [√] Connected device (1 available) [√] Network resources • No issues found! ```