5 min read
Fix the shared_preferences release build crash on Android
shared_preferences_android 2.5.0 keeps Flutter release builds on the splash screen. What causes the shared_preferences release build crash, and two fixes.
By Yevhen Beshkarov
- troubleshooting
- android
On 17 October 2026, shared_preferences_android 2.5.0 came out, and Flutter apps that use shared_preferences stopped starting in Android release builds. The app stays on the splash screen, and logcat has a NoSuchMethodError. Debug and profile builds work, and so does iOS, so a CI that builds only debug stays green. This shared_preferences release build crash comes from R8, which renames Kotlin classes that the new version looks up by name. You can go back to the previous version or add six keep rules to the app. The fix upstream is a pull request that waits for review.
NoSuchMethodError in SharedPreferencesAsyncApiRegistrar.getAll
With SharedPreferencesAsync or SharedPreferencesWithCache, the first call to the preferences throws:
E flutter: Unhandled Exception: PlatformException(PlatformException, java.lang.NoSuchMethodError: no non-static method "Lio/flutter/plugins/sharedpreferences/SharedPreferencesAsyncApiRegistrar;.getAll(Ljava/util/List;Lio/flutter/plugins/sharedpreferences/SharedPreferencesPigeonOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;"With the legacy SharedPreferences.getInstance(), the class in the message is SharedPreferencesApiRegistrar:
PlatformException(PlatformException, java.lang.NoSuchMethodError: no non-static method "Lio/flutter/plugins/sharedpreferences/SharedPreferencesApiRegistrar;.getAll(Ljava/lang/String;Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;"When the app opens the preferences in main(), before runApp(), the future fails and runApp() never runs. The app keeps showing its splash screen, and no crash dialog comes up. An app that opens the preferences later fails on the screen that reads them.
When it fails: shared_preferences_android 2.5.0 and R8
Check which version pub resolved, in the directory of the app:
flutter pub deps | grep shared_preferences_androidYou have this case when:
- it prints
shared_preferences_android 2.5.0.shared_preferences2.5.5 allows any 2.x of its Android side from 2.3.4 on, so aflutter pub upgrade, or a firstflutter pub getafter 17 October, brings 2.5.0 in; - you build for Android in release:
flutter build apk,flutter build appbundleorflutter run --release. Flutter turns R8 on for every release build, and the app bundle that you upload to Google Play has the bug too; - debug and profile builds of the same app work.
iOS and macOS use shared_preferences_foundation, and the other platforms have packages of their own, so only Android is affected.
Fix 1: keep shared_preferences_android below 2.5.0
flutter pub add 'shared_preferences_android:>=2.4.28 <2.5.0'The app then depends on the Android side of the package directly, and pub goes back to 2.4.28, which talks to Kotlin through a method channel as before. The rest of the app stays as it is.
Once a fixed version is out, remove the dependency and upgrade the package. Without the upgrade, pubspec.lock keeps 2.4.28:
flutter pub remove shared_preferences_android
flutter pub upgrade shared_preferences_androidFix 2: keep the Kotlin classes with R8 rules
To stay on 2.5.0, tell R8 to keep the classes. Put these lines into android/app/proguard-rules.pro, and create the file if the app has none:
-keep class kotlin.Unit { *; }
-keep class kotlin.Result { *; }
-keep class kotlin.Result$Failure { *; }
-keep class kotlin.coroutines.Continuation { *; }
-keep class kotlin.coroutines.intrinsics.CoroutineSingletons { *; }
-keep class kotlin.coroutines.intrinsics.IntrinsicsKt { *; }The Gradle plugin of Flutter adds this file to the release build when it exists, so build.gradle.kts stays as it is. The rules are those of Step 5 in the native interop guide of Pigeon, the tool that generates the code of the plugin.
After either fix, start the release build again with flutter run --release.
Why the shared_preferences release build crash happens
Version 2.5.0 moved the plugin from method channels to the native interop of Pigeon (flutter/packages#12329). The Dart code now calls the Kotlin code of the plugin through JNI, and JNI finds classes and methods at run time by their names and descriptors.
R8 renames the classes that, as far as it can see, nothing refers to by name. The generated Kotlin classes of the plugin have @Keep since flutter/packages#13151, so SharedPreferencesAsyncApiRegistrar keeps its name. Its methods are suspend functions, though, and their last parameter is a kotlin.coroutines.Continuation. Nothing keeps that class. From build/app/outputs/mapping/release/mapping.txt of a release build:
kotlin.coroutines.Continuation -> o1.d:R8 renames the class and rewrites the descriptor of getAll with the new name. The Dart side still asks for a method that ends with Lkotlin/coroutines/Continuation;, and that method no longer exists. The same goes for kotlin.Unit, kotlin.Result and the coroutine classes that package:jni 1.1.0 looks up by name in lib/src/kotlin.dart. The plugin has no consumer rules for R8, and the rules of jni keep only its own package, com.github.dart_lang.jni.**.
Debug and profile builds run without R8, so every name stays and the calls work.
Why a green CI with debug builds missed it
The release APK builds without an error and fails only when its code runs. I found it by chance on the day 2.5.0 came out: while checking an unrelated change in SMF, I started a release build on an emulator, and it stayed on the splash screen. An app from a plain flutter create did the same. The CI of SMF builds its generated apps for Android and starts them on an emulator, on every pull request and every night, and every Android build there was a debug build. So it stayed green.
Your users get a release build, so start one before they do: once a day in CI, or by hand before each release. Build it with flutter build apk --release, install it on an emulator and check that the first screen shows. R8 runs only in release builds, and that is the only place where a bug like this one shows up.
Where the upstream fix stands
- flutter/flutter#194649 is the issue I opened on 17 October, with the steps to reproduce. Within a day, other apps linked to it from their own fixes.
- flutter/packages#13311 is my pull request. It adds the six rules to
shared_preferences_androidas consumer rules, which R8 applies in every app that uses the plugin. On 18 October it waits for a review. - dart-lang/native#3732 is about shipping the rules in
jniitself, so that every plugin on the native interop of Pigeon gets them.
When a fixed version of shared_preferences_android is out, drop the constraint or the rules, and start a release build once more.
If another plugin of your app moves to the native interop of Pigeon and its release build fails with a NoSuchMethodError or a ClassNotFoundException that names a kotlin class, try the same rules first.
How SMF keeps generated apps on shared_preferences_android 2.4.28
I ran into this while building SMF, a free, open-source Flutter project generator. Since version 0.4.2, released on 17 October, its shared_preferences module keeps shared_preferences_android below 2.5.0 in every app it generates. The guide for coding agents in the app says why, and asks them to start a release build on Android before they raise the constraint. I'm also adding a CI job to SMF that starts a release build of a generated app on an Android emulator.
On iOS I hit the same kind of trap, where debug works and the release archive fails: the Crashlytics build phase of FlutterFire breaks flutter build ipa in apps with Swift Package Manager. The fix for the flutterfire upload-crashlytics-symbols error is in an earlier post.