By Pratik Butani
Google Play requires app updates to target API level 36 (Android 16) by
August 31, 2026 (an extension to Nov 1, 2026 is requestable). This applies
identically to native Android and Flutter apps — Flutter builds through the
same Android Gradle project under android/.
Goal: raise the target/compile SDK, confirm the toolchain actually supports it, identify and fix any API-36-specific behavior changes that affect this app, and ship a verified working release — not just a version number that happens to compile. A clean build proves almost nothing here; several of the real risks below compile perfectly and only fail on specific device configurations at runtime.
- Find current
compileSdk/targetSdk(native:app/build.gradle; Flutter:android/app/build.gradle). - Find current AGP version, Gradle wrapper version, and (Flutter only)
flutter --version/ Flutter Gradle plugin version. - Don't assume compatibility from a table — try compiling against SDK 36 and see whether it's a clean success, a soft "untested compileSdk" warning, or a hard failure. All three are possible depending on the AGP version in use, and it's cheap to just check.
- Install
platforms;android-36(latest point release) and matching build-tools viasdkmanager/Android Studio's SDK Manager. - If the compile check in Step 1 hard-fails, upgrade AGP + the paired Gradle wrapper version per Google's compatibility matrix — as its own commit, separate from the SDK bump, so a toolchain-upgrade regression is easy to isolate from an SDK-bump regression.
- Set
compileSdk 36/targetSdkVersion 36(or Flutter equivalent). - Build release, not just debug:
./gradlew bundleRelease/flutter build appbundle --release. R8/minification, resource shrinking, and signing only get exercised in a release build. - A soft "untested compileSdk" warning with an otherwise clean build is fine to ship (pending device testing below); a hard error means resolve Step 2 first.
Read https://developer.android.com/about/versions/16/behavior-changes-16 directly before acting — don't implement from memory or from a checklist someone wrote months ago; Google revises these lists. As of this writing, the ones most likely to actually bite:
-
Large-screen orientation/resizability restrictions are ignored on displays with smallest width ≥ 600dp (tablets, unfolded foldables, desktop windowing).
android:screenOrientation,android:resizeableActivity="false", and aspect-ratio declarations all stop being honored there once you target 36.- If the app is genuinely phone-only and untested on large screens,
don't let this slide silently into "resizable on tablets we never
designed for." Two real options:
a. Temporary opt-out:
<property android:name= "android.window.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY" android:value="true" />in<application>(or a specific<activity>). Removed entirely once the app targets API 37 — this buys time, it isn't a permanent fix. b. Real adaptive-layout work — schedule as its own effort, don't fold it into "just bump the SDK." - Test on an actual tablet/foldable or a large-screen emulator
(
pixel_tabletdevice profile) before shipping either way. A phone simulated viaadb shell wm size/wm densitycan substitute in a pinch, but some OEM skins block those commands outside root — verify the override actually took effect before trusting the test.
- If the app is genuinely phone-only and untested on large screens,
don't let this slide silently into "resizable on tablets we never
designed for." Two real options:
a. Temporary opt-out:
-
Edge-to-edge enforcement (started at API 35, re-verify at 36). Apps without
enableEdgeToEdge()+WindowInsetsCompat/ViewCompat.setOnApplyWindowInsetsListenerhandling on top-level screens can end up with content under system bars. Grep for legacycolorPrimaryDark-based status bar tinting,android:statusBarColor/navigationBarColortheme attributes, andsetSystemUiVisibility/SYSTEM_UI_FLAG_*— these are what Play Console's "deprecated APIs for edge-to-edge" warning is actually about. -
16 KB memory page size support — only relevant if the app ships native code (NDK, or a dependency that does). Zero native libraries = automatically fine; otherwise every native
.soneeds 16 KB alignment (NDK r27+ defaults to this). -
Predictive back gesture — if
android:enableOnBackInvokedCallback= "true"is set, re-verify custom back-handling (fragment stacks, WebView history, fullscreen player exits) still behaves correctly. -
Whatever else the official page calls out at the time you do this — read it, don't skip straight to a checklist.
Don't assume "no library upgrades needed" from one project applies to another — check explicitly:
- Google Play Services / Firebase BOM — old versions can have hard compileSdk ceilings or lack API-36 behavior fixes.
- AndroidX (
androidx.core,androidx.activity,androidx.windowespecially) — the edge-to-edge/large-screen APIs above live here; old versions may not have them. - Gradle-plugin-dependent tooling (Crashlytics, Google Services plugin, etc.) — recheck compatibility if AGP/Gradle got bumped in Step 2.
- Native/NDK dependencies — re: 16 KB pages, above.
- Flutter only: native-Android plugins can pin their own compileSdk
floor/ceiling in
android/build.gradleand block the whole app's bump even when your own code is fine. Runflutter pub outdated, and if the build fails, check plugins with native Android code individually before assuming it's your code.
A major SDK bump is a good moment to audit proguard-rules.pro:
- Remove
-keeprules for packages that don't exist anywhere in the current dependency graph (verify with a grep +./gradlew dependenciesdump — don't delete on assumption). - Remove rules duplicating what a library's own bundled
consumer-rules.proalready provides (most modern libraries ship these — AndroidX, Retrofit, Gson, Glide). - Leave anything protecting your own data-model classes accessed reflectively (Firestore/Gson/Moshi/Room) strictly alone.
- Verify with an actual on-device smoke test of the release build after any ProGuard change — a successful build proves nothing about runtime reflection paths.
- Bump
versionCode/versionNamefor every build you intend to actually upload, even a small follow-up fix to the same release attempt. Play rejects re-uploading an already-consumed versionCode, and it's cheaper to over-increment than to guess whether a prior build was actually uploaded. - Do this work on its own branch, isolated from whatever's currently shipping — don't mix an SDK-target bump with unrelated feature work.
- Confirm the release build is actually signed correctly
(
jarsigner -verify) — a build that compiles but isn't properly signed won't upload.
In order of value for time spent:
- Full release-mode build succeeds (not debug-only).
- Install on a real device (or an emulator matching the app's actual minSdk floor) and smoke-test: launch, primary content flow, any screen with heavy AppCompat/Material form widgets (first thing a ProGuard regression breaks), and platform-integration features (deep links, notifications, downloads, sign-in, purchases).
- If any screen is orientation-locked: test on a large-screen device or emulator specifically. This is the step most likely to get skipped and most likely to actually break something.
- After a real Play Console upload, re-check its "App quality"/pre-launch warnings — several of these (edge-to-edge, bitmap/R8 optimization, PiP suggestion) only surface there, not in local testing.
- Don't treat "it compiles" as "it works" — the large-screen and edge-to-edge risks above are both silent at compile time.
- Don't guess manifest property names or behavior-change specifics from memory — confirm against current official docs at the time of the work.
- Don't bundle this into an already-in-flight urgent release branch if avoidable.
- Don't skip large-screen testing because your user base is "mostly phones" — Play Console's compliance check doesn't know your user distribution, and a broken tablet layout is worse than a slightly overdue targetSdk.