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

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

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

  • checkReleaseAarMetadata with AndroidX dependencies requiring compileSdk 34+
  • NDK not configured properly or unsupported 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

  1. Plugins stuck on compileSdk 33 E.g.: awesome_notifications_core, firebase_messaging, etc. → they don't compile against Android 34+.

  2. Outdated tool versions

    • Gradle < 8.6
    • Android Gradle Plugin < 8.4
    • JDK < 17 These versions don't recognize the new NDK/ABI rules.
  3. Improper propagation of SDK properties Flutter does not automatically enforce compileSdkVersion on 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

ToolMinimum version
Flutter SDK3.24.3
Gradle wrapper8.7
Android Gradle Plugin (AGP)8.6.1
Kotlin1.9.24
JDK17
NDKr27+

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

DomainBefore (Android 14)Now (Android 15)
Memory page size4 KB16 KB
Minimum ABIsarmeabi-v7a, arm64-v8aarm64-v8a, x86_64
NDKr25+r27+
JDK1117
AGP8.18.6
compileSdk3335/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

Tags

  • flutter

  • iOS

  • android

  • android 15

  • google play

  • gradle

  • kotlin

  • ndk

This article was posted on

Comments

Loading...

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