Understanding and Successfully Navigating the Android 15 Transition: From 4KB to 16KB, the Change That Blocks Publishing Your Flutter Apps on Google Play

Sommaire
Since Android 15 (API 35), Google is imposing a quiet but massive change: memory page sizes are being increased from 4 KB to 16 KB on some architectures. A low-level detail... which will eventually block the publication of many apps on Google Play.
1. What changes with Android 15
Historically, the majority of Android devices used a 4 KB memory page. Android 15 officially introduces support for 16 KB pages on ARM v9 processors and some x86_64 variants.
Why this change?
- Performance: fewer page faults, better memory handling on large workloads.
- Security: stricter memory alignment for some protections.
- Future hardware: new ARM SoCs enforce this granularity.
Consequence for your apps
Native binaries (.so files in your APK/AAB) must be aligned to 16 KB, and built with a recent NDK (r27+).
Even if your Flutter app has no native code, it bundles native libraries through its dependencies (notably plugins).
2. The errors and symptoms you will encounter
Here are typical problems Flutter developers face when updating:
Build errors
checkReleaseAarMetadatawith AndroidX dependencies requiringcompileSdk 34+NDK not configured properlyorunsupported page size- Build OK on Android 14, but rejected by Google Play during submission
Runtime errors
- Crash on launch on an Android 15 device:
libflutter.so is not aligned for 16KB page size
Frequent causes
-
Plugins stuck on
compileSdk 33E.g.:awesome_notifications_core,firebase_messaging, etc. → they don't compile against Android 34+. -
Outdated tool versions
- Gradle < 8.6
- Android Gradle Plugin < 8.4
- JDK < 17 These versions don't recognize the new NDK/ABI rules.
-
Improper propagation of SDK properties Flutter does not automatically enforce
compileSdkVersionon plugins. Each Gradle submodule can define its own.
Result: your app targets Android 35, but a plugin still compiles with 33 → build failure.
4. The fundamentals to update
Recommended minimum versions
| Tool | Minimum version |
|---|---|
| Flutter SDK | 3.24.3 |
| Gradle wrapper | 8.7 |
| Android Gradle Plugin (AGP) | 8.6.1 |
| Kotlin | 1.9.24 |
| JDK | 17 |
| NDK | r27+ |
5. Typical configuration compatible with Android 15
android/gradle.properties
android.useAndroidX=true
android.enableJetifier=true
flutter.minSdkVersion=23
flutter.targetSdkVersion=35
flutter.compileSdkVersion=36
org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8
android/app/build.gradle.kts
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
kotlin("android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
val minSdk = providers.gradleProperty("flutter.minSdkVersion").orNull?.toInt() ?: 23
val targetSdk = providers.gradleProperty("flutter.targetSdkVersion").orNull?.toInt() ?: 35
val compileSdk = providers.gradleProperty("flutter.compileSdkVersion").orNull?.toInt() ?: 36
android {
namespace = "com.yourcompany.yourapp"
compileSdk = compileSdk
ndkVersion = "29.0.13846066"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions { jvmTarget = "17" }
defaultConfig {
applicationId = "com.yourcompany.yourapp"
minSdk = minSdk
targetSdk = targetSdk
versionCode = flutter.versionCode
versionName = flutter.versionName
ndk {
abiFilters += listOf("arm64-v8a", "x86_64") // 64 bits only = compatible 16KB
}
}
packaging {
jniLibs.useLegacyPackaging = false
}
bundle {
language.enableSplit = false
density.enableSplit = false
abi.enableSplit = true
}
}
flutter {
source = "../.."
}
And above all: the magic block
The problem: even if you configure compileSdkVersion in your app, each Flutter plugin can set its own compileSdkVersion in its build.gradle. A single plugin left at version 33 can block the whole build.
Solution: in android/build.gradle.kts, add this block that forces all subprojects (plugins) to use the same versions:
In android/build.gradle.kts:
subprojects {
afterEvaluate {
val ext = extensions.findByName("android") ?: return@afterEvaluate
val cs = providers.gradleProperty("flutter.compileSdkVersion").orNull?.toInt()
val ts = providers.gradleProperty("flutter.targetSdkVersion").orNull?.toInt()
@Suppress("UNCHECKED_CAST")
(ext as? com.android.build.gradle.BaseExtension)?.apply {
if (cs != null) setCompileSdkVersion(cs)
defaultConfig {
if (ts != null) targetSdkVersion(ts)
}
}
}
}
Why afterEvaluate? Because plugins configure their Android extension at different times. We wait until all plugins have finished their configuration before modifying them.
This block guarantees that all your plugins use the same compileSdk/targetSdk as your app, avoiding version conflicts.
6. Clean and recompile
flutter clean
rm -rf android/.gradle ~/.gradle/caches
flutter pub upgrade
flutter build apk -v
7. Final checks
✅ The build succeeds without errors
✅ The APK/AAB contains the ABIs arm64-v8a and x86_64
✅ You target targetSdkVersion 35+
✅ Native binaries are correctly aligned (16 KB compatible)
8. Summary: the new Android 15 paradigm
| Domain | Before (Android 14) | Now (Android 15) |
|---|---|---|
| Memory page size | 4 KB | 16 KB |
| Minimum ABIs | armeabi-v7a, arm64-v8a | arm64-v8a, x86_64 |
| NDK | r25+ | r27+ |
| JDK | 11 | 17 |
| AGP | 8.1 | 8.6 |
| compileSdk | 33 | 35/36 |
In conclusion
The move to 16 KB page size marks a deep modernization of the Android ecosystem, which Flutter must closely follow.
The visible symptoms (build errors, broken plugins, Google Play rejections) are in fact alignment signals: the entire pipeline (Gradle, AGP, NDK, plugins) must be upgraded coherently.
If your Flutter project is already up to date, these steps ensure Android 15 compatibility and avoid future crashes related to ARM v9 hardware.
Last-minute tip: if you don't have time to become compliant immediately, you can request an extension from Google Play, but it's not guaranteed. It's better to anticipate!
Additional resources
Comments
Loading...