After null-safety migration and update Flutter sdk from 2.8.0 to 3.3.4, my app crashes on launch in release mode. It shows a black screen during 2 secs and then it close. Before this migration, works fine. I know the problem is on localizations because if I comment supportedLocales, localizationsDelegates and localeResolutionCallback parameter from MaterialApp widget, the Splash screen is shown.
This problem is the same for Android and iOS platforms.
[✓] Flutter (Channel stable, 3.3.4, on macOS 12.6 21G115 darwin-x64, locale en-GB)
• Flutter version 3.3.4 on channel stable at /Users/sissa/fvm/versions/3.3.4
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision eb6d86ee27 (9 days ago), 2022-10-04 22:31:45 -0700
• Engine revision c08d7d5efc
• Dart version 2.18.2
• DevTools version 2.15.0
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Users/sissa/Library/Android/sdk
• Platform android-33, build-tools 30.0.3
• ANDROID_HOME = /Users/sissa/Library/Android/sdk
• Java binary at: /Applications/Android Studio 3.app/Contents/jre/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 14.0.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 14A400
• CocoaPods version 1.11.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2021.3)
• Android Studio at /Applications/Android Studio 3.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
[✓] VS Code (version 1.40.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension can be installed from:
🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter
[✓] Connected device (3 available)
• PRA LX1 (mobile) • 9DC7N17707001642 • android-arm64 • Android 7.0 (API 24)
• macOS (desktop) • macos • darwin-x64 • macOS 12.6 21G115 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 106.0.5249.119
[✓] HTTP Host Availability
• All required HTTP hosts are available
• No issues found!
@override
Widget build(BuildContext context) {
return KeyedSubtree(
key: key,
child: provider.ChangeNotifierProvider<BluetoothConnectionProvider>.value(
value: bluetoothConnectionProvider as BluetoothConnectionProvider,
child: LiveCycleManager(
child: MaterialApp(
navigatorKey: NavigationService.navigatorKey,
supportedLocales: [
Locale('en', 'GB'),
Locale('es', 'ES'),
Locale('fr', 'FR'),
Locale('ca', 'CA'),
Locale('it', 'IT'),
Locale('de', 'DE'),
Locale('en', 'ES'),
Locale('fr', 'ES'),
Locale('ca', 'ES'),
Locale('it', 'ES'),
Locale('de', 'ES'),
],
localizationsDelegates: [
AppLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
localeResolutionCallback: (deviceLocale, supportedLocales) {
for (Locale locale in supportedLocales) {
// if device language is supported by the app,
// just return it to set it as current app language
if (supportedLocales.contains(deviceLocale)) {
locator<Resources>().prefs!.preferences!.setString(
kCurrentLocaleLanguage, deviceLocale!.languageCode);
return deviceLocale;
}
}
// if device language is not supported by the app,
// the app will set it to english but return this to set to Bahasa instead
locator<Resources>()
.prefs!
.preferences!
.setString(kCurrentLocaleLanguage, 'en');
return Locale('en', 'EN');
},
theme: getAppTheme(),
routes: {
SplashScreen.routeName: (context) => SplashScreen(),
LoginScreen.routeName: (context) => LoginScreen(),
ResetPasswordScreen.routeName: (context) =>
ResetPasswordScreen(),
RegisterScreen.routeName: (context) => RegisterScreen(),
// .....
},
home: FlavorBanner(
child: SplashScreen(),
flavor: widget.appConfig!.flavor,
),
debugShowCheckedModeBanner: false),
),
),
);
}
class AppLocalizations {
final Locale locale;
AppLocalizations(this.locale);
// Helper method to keep the code in the widgets concise
// Localizations are accessed using an InheritedWidget "of" syntax
static AppLocalizations? of(BuildContext context) {
return Localizations.of<AppLocalizations>(context, AppLocalizations);
}
// Static member to have a simple access to the delegate from the MaterialApp
static const LocalizationsDelegate<AppLocalizations> delegate =
_AppLocalizationsDelegate();
Map<String, String>? _localizedStrings;
Future<bool> load() async {
// Load the language JSON file from the "lang" folder
printLog('LOCALE_SAMCLA:${locale.languageCode}');
String jsonString =
await rootBundle.loadString('i18n/${locale.languageCode}.json');
Map<String, dynamic> jsonMap = json.decode(jsonString);
_localizedStrings = jsonMap.map((key, value) {
return MapEntry(key, value.toString());
});
return true;
}
// This method will be called from every widget which needs a localized text
String? translate(String? key) {
if (null == key)
return _localizedStrings?['LOC_ACTION_COMPLETED_NOT_SUCCESSFULLY'];
String? text = _localizedStrings?[key];
return text ?? _localizedStrings?['LOC_ACTION_COMPLETED_NOT_SUCCESSFULLY'];
}
}
// LocalizationsDelegate is a factory for a set of localized resources
// In this case, the localized strings will be gotten in an AppLocalizations object
class _AppLocalizationsDelegate
extends LocalizationsDelegate<AppLocalizations> {
// This delegate instance will never change (it doesn't even have fields!)
// It can provide a constant constructor.
const _AppLocalizationsDelegate();
@override
bool isSupported(Locale locale) {
// Include all of your supported language codes here
return ['en', 'ca', 'fr', 'es', 'it', 'de'].contains(locale.languageCode);
}
@override
Future<AppLocalizations> load(Locale locale) async {
// AppLocalizations class is where the JSON loading actually runs
AppLocalizations localizations = new AppLocalizations(locale);
await localizations.load();
return localizations;
}
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
Rejecting re-init on previously-failed class java.lang.Class<io.flutter.embedding.engine.FlutterJNI$$ExternalSyntheticLambda0>: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/graphics/ImageDecoder$OnHeaderDecodedListener;
at io.flutter.embedding.engine.FlutterJNI io.flutter.embedding.engine.FlutterJNI$Factory.provideFlutterJNI() (FlutterJNI.java:123)
at void io.flutter.FlutterInjector$Builder.fillDefaults() (FlutterInjector.java:169)
at io.flutter.FlutterInjector io.flutter.FlutterInjector$Builder.build() (FlutterInjector.java:179)
at io.flutter.FlutterInjector io.flutter.FlutterInjector.instance() (FlutterInjector.java:57)
at void io.flutter.embedding.engine.FlutterEngine.<init>(android.content.Context, io.flutter.embedding.engine.loader.FlutterLoader, io.flutter.embedding.engine.FlutterJNI, io.flutter.plugin.platform.PlatformViewsController, java.lang.String[], boolean, boolean) (FlutterEngine.java:289)
at void io.flutter.embedding.engine.FlutterEngine.<init>(android.content.Context, java.lang.String[], boolean, boolean) (FlutterEngine.java:207)
at void io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setupFlutterEngine() (FlutterActivityAndFragmentDelegate.java:271)
at void io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(android.content.Context) (FlutterActivityAndFragmentDelegate.java:180)
at void io.flutter.embedding.android.FlutterActivity.onCreate(android.os.Bundle) (FlutterActivity.java:498)
at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6915)
at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1123)
at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2746)
at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2864)
at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1567)
at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
at void android.os.Looper.loop() (Looper.java:156)
at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6523)
at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:942)
at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:832)
Caused by: java.lang.ClassNotFoundException: Didn't find class "android.graphics.ImageDecoder$OnHeaderDecodedListener" on path: DexPathList[[zip file "/data/app/com.xxxxx.xxxxx.dev-2/base.apk"],nativeLibraryDirectories=[/data/app/com.xxxxx.xxxxx.dev-2/lib/arm64, /data/app/com.xxxxx.xxxxx.dev-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]
at java.lang.Class dalvik.system.BaseDexClassLoader.findClass(java.lang.String) (BaseDexClassLoader.java:56)
at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String, boolean) (ClassLoader.java:380)
at java.lang.Class java.lang.ClassLoader.loadClass(java.lang.String) (ClassLoader.java:312)
at io.flutter.embedding.engine.FlutterJNI io.flutter.embedding.engine.FlutterJNI$Factory.provideFlutterJNI() (FlutterJNI.java:123)
at void io.flutter.FlutterInjector$Builder.fillDefaults() (FlutterInjector.java:169)
at io.flutter.FlutterInjector io.flutter.FlutterInjector$Builder.build() (FlutterInjector.java:179)
at io.flutter.FlutterInjector io.flutter.FlutterInjector.instance() (FlutterInjector.java:57)
at void io.flutter.embedding.engine.FlutterEngine.<init>(android.content.Context, io.flutter.embedding.engine.loader.FlutterLoader, io.flutter.embedding.engine.FlutterJNI, io.flutter.plugin.platform.PlatformViewsController, java.lang.String[], boolean, boolean) (FlutterEngine.java:289)
at void io.flutter.embedding.engine.FlutterEngine.<init>(android.content.Context, java.lang.String[], boolean, boolean) (FlutterEngine.java:207)
at void io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.setupFlutterEngine() (FlutterActivityAndFragmentDelegate.java:271)
at void io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.onAttach(android.content.Context) (FlutterActivityAndFragmentDelegate.java:180)
at void io.flutter.embedding.android.FlutterActivity.onCreate(android.os.Bundle) (FlutterActivity.java:498)
at void android.app.Activity.performCreate(android.os.Bundle) (Activity.java:6915)
at void android.app.Instrumentation.callActivityOnCreate(android.app.Activity, android.os.Bundle) (Instrumentation.java:1123)
at android.app.Activity android.app.ActivityThread.performLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent) (ActivityThread.java:2746)
at void android.app.ActivityThread.handleLaunchActivity(android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:2864)
at void android.app.ActivityThread.-wrap12(android.app.ActivityThread, android.app.ActivityThread$ActivityClientRecord, android.content.Intent, java.lang.String) (ActivityThread.java:-1)
at void android.app.ActivityThread$H.handleMessage(android.os.Message) (ActivityThread.java:1567)
at void android.os.Handler.dispatchMessage(android.os.Message) (Handler.java:105)
at void android.os.Looper.loop() (Looper.java:156)
at void android.app.ActivityThread.main(java.lang.String[]) (ActivityThread.java:6523)
at java.lang.Object java.lang.reflect.Method.invoke!(java.lang.Object, java.lang.Object[]) (Method.java:-2)
at void com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run() (ZygoteInit.java:942)
at void com.android.internal.os.ZygoteInit.main(java.lang.String[]) (ZygoteInit.java:832)
Finally I found the solution but I don't understand it. The problem was on routes definition in app.dart. All classes have to be constants.
Before
routes: {
SplashScreen.routeName: (context) => SplashScreen(),
LoginScreen.routeName: (context) => LoginScreen(),
}
After
routes: {
SplashScreen.routeName: (context) => const SplashScreen(),
LoginScreen.routeName: (context) => const LoginScreen(),
}
If somebody knows why it worked, feel free to explain it to us ;)