Independent product studio · Swedenhello@karelialabs.com

Guide

Flutter and Google Play's 16 KB page size rule: find the library that fails

Short answerIn the two real Flutter apps we checked, Flutter's own libraries passed and an AndroidX library failed Google's RELRO rule. Update Flutter and every plugin with native code, rebuild, and check each 64-bit library for both alignment and RELRO.

Updated

What the Play Console message means

Google's page size guide says that all apps targeting Android 15 (API level 35) and higher must support 16 KB memory page sizes on 64-bit devices on Google Play, and that from February 1, 2027 updates without 16 KB support can't be released. Play Console shows a compatibility warning for a bundle that doesn't support them.

Google Play already requires new apps and updates to target API level 36 (Google's target API rule; Wear OS, Android Automotive OS, Android TV and Android XR have lower levels). So for a phone or tablet app, the 16 KB rule applies to every update that contains native code.

The warning is about native libraries, the .so files for the 64-bit ABIs arm64-v8a and x86_64. A library fails when its LOAD segments are aligned to 4 KB instead of 16 KB, or when it is stored uncompressed and does not start on a 16 KB boundary in the zip. Every Flutter app for Android has native code: the engine ships as libflutter.so, your Dart code is compiled ahead of time into libapp.so, and plugins can add libraries of their own.

What we found in two real Flutter apps

We ran Ship Check on two open-source Flutter apps from F-Droid, LocalSend 1.18.2 and Saber 1.37.1 (APKs downloaded on 10 November 2026):

  • In both, every 64-bit library is 16 KB aligned, libflutter.so and libapp.so included, and the uncompressed libraries start on 16 KB boundaries in the APK.
  • In both, a copy of libdatastore_shared_counter.so from AndroidX DataStore fails Google's RELRO rule: GNU_RELRO ends at 0x6000, which is not a multiple of 0x4000, and writable data follows it in the same LOAD segment.
  • Libraries from other native plugins, such as librust_lib_localsend_app.so and libpdfium.so, are not in Ship Check's library map and are listed by file name.

So a Flutter app can pass an alignment check and still break on a 16 KB device. Check RELRO as well.

Ship Check report for a real Flutter app: every 64-bit library passes 16 KB alignment but one fails the RELRO rule
A real Flutter app: aligned, and one library still fails RELRO

Find the library that fails

  1. List the native libraries in the build. In a macOS, Linux or Git Bash terminal: unzip -l app-release.aab | grep '\.so$'. In Windows PowerShell: tar -tf .\app-release.aab | Select-String '\.so$'. In an AAB they are under base/lib/<abi>/, in an APK under lib/<abi>/.
  2. Look at the arm64-v8a and x86_64 folders; the rule is about 64-bit devices. Match each file name against the table below to see whether it comes from the engine, a plugin or an SDK.
  3. Check how each library is aligned. Ship Check (below) checks all of them at once. In Android Studio, Build > Analyze APK... shows an Alignment column that warns about every library that is not 16 KB aligned.
LibraryComes fromWhat to do
libflutter.soThe Flutter engineUpdate Flutter and rebuild.
libapp.soYour Dart code, compiled ahead of timeRebuild with the updated Flutter.
libdartjni.soThe jni Dart packageUpdate the plugin that uses it to a release built with NDK r28 or newer.
libdatastore_shared_counter.soAndroidX DataStore, pulled in by a pluginUpdate the androidx.datastore dependency, or the plugin that pulls it in, to a release built with NDK r28 or newer.
libc++_shared.soThe NDK's C++ runtimeGoogle says copies from NDK r26 and lower are not always 16 KB aligned. Update the plugin that ships it.
Any other .so fileA plugin with native codeUpdate the plugin. If no 16 KB build exists, ask its maintainer.

Fix it

We did not find a Flutter release note that names the first Flutter version with 16 KB support, so this guide gives no version number. What Google's page and Ship Check's library map say:

  1. Update Flutter and rebuild.
  2. Update every plugin that ships its own native library. A library has to be built with NDK r28 or newer, or with -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384 on an older NDK, to pass.
  3. If your project pins its own Android Gradle Plugin version, use 8.5.1 or higher, so uncompressed libraries are 16 KB aligned in the zip.
  4. Build the AAB again and check it.

Check the build before you upload it

  1. Open Ship Check and drop the .aab or .apk on the page. It is free, runs in your browser and nothing is uploaded.
  2. Read the 16 KB results. Every 64-bit library is checked for LOAD segment alignment and for Google's RELRO rule, and each failing library is named with the engine, plugin or SDK that ships it where Ship Check knows it. A library it does not know is listed by file name.
  3. On a build server, run Ship Check CLI: node shipcheck.mjs app-release.aab --fail-on=error. It exits with code 1 when a check fails, so the pipeline stops before the upload. Add --date 2027-02-01 to see the build as it will be judged on that day.

Alignment is not the only test. Google's page also has a rule for the GNU_RELRO segment: if it ends inside a 16 KB page that also holds writable data, the app crashes with a segmentation fault (SIGSEGV) on a 16 KB device when that data is written. Google says this typically happens with libraries built with NDK r27 or lower without the 16 KB flags.

Ship Check reads what is in the file. It cannot see your Play Console, and a passing report does not guarantee that Google Play accepts the app.

The same rule in other engines

FAQ

My build passes zipalign. Why does Ship Check fail it?

zipalign and LOAD segment checks do not look at RELRO. Google's page has a separate rule for the GNU_RELRO segment, and the two Flutter apps we checked fail only that one.

Which Flutter version do I need?

We did not find a Flutter release note that names one. Update Flutter and your plugins, rebuild, and check the result.

Which plugin brings in AndroidX DataStore?

The library itself does not say. Look for the plugins in your project that depend on androidx.datastore and update them.

Is my build uploaded anywhere?

No. Ship Check reads the file in your browser, and Ship Check CLI reads it on your own machine.

More guides

More from Karelia Labs