Skip to content

flutter build apk --flavor silently packages stale Dart code: merge<Variant>JniLibFolders stays UP-TO-DATE after Dart changes (3.44 regression) #187553

Description

@sunnykinger

Steps to reproduce

flutter create flavor_stale_repro
cd flavor_stale_repro

Add product flavors to android/app/build.gradle.kts (inside the android { } block, before buildTypes):

flavorDimensions += "default"
productFlavors {
    create("prod") { dimension = "default" }
    create("dev") { dimension = "default" }
}

Then:

# 1. Add a marker to lib/main.dart, e.g. first line of main():
#      print('probe-AAAA');
flutter build apk --release --flavor prod        # build 1 — correct

# 2. Change the Dart code:  probe-AAAA  ->  probe-BBBB
flutter build apk --release --flavor prod        # build 2 — INCREMENTAL, reports success

# 3. Inspect what was actually packaged:
unzip -p build/app/outputs/flutter-apk/app-prod-release.apk lib/arm64-v8a/libapp.so | strings | grep -c probe-BBBB   # → 0  (expected 1)
unzip -p build/app/outputs/flutter-apk/app-prod-release.apk lib/arm64-v8a/libapp.so | strings | grep -c probe-AAAA   # → 1  (expected 0)

Expected results

Build 2 packages the recompiled Dart AOT snapshot (probe-BBBB).

Actual results

Build 2 reports ✓ Built …app-prod-release.apk but the APK contains the previous build's Dart code (probe-AAAA). Every subsequent incremental --flavor release build keeps shipping the snapshot from the variant's first build until flutter clean.

Where it breaks (verified by content, not timestamps)

The Dart compile chain is healthy; the AGP hand-off is not:

Stage Content after build 2
intermediates/flutter/prodRelease/arm64-v8a/app.so (AOT output) fresh — contains probe-BBBB
intermediates/flutter/prodRelease/jniLibs/arm64-v8a/libapp.so (Flutter→AGP hand-off) fresh — contains probe-BBBB
intermediates/merged_jni_libs/prodRelease/mergeProdReleaseJniLibFolders/out/…/libapp.so STALE — still probe-AAAA, mtime frozen at build 1
merged_native_libs → stripped_native_libs → APK faithfully repackage the stale bytes (with fresh mtimes)

i.e. :app:mergeProdReleaseJniLibFolders is considered UP-TO-DATE even though the Flutter jniLibs directory it consumes has changed content. Deleting merged_native_libs + stripped_native_libs does NOT help (they re-run but read the frozen merged_jni_libs); deleting merged_jni_libs/<variant> as well produces a correct APK.

Scope / what is NOT affected

  • No flavors → not affected. Same machine, same SDKs, plain flutter build apk --release propagates Dart changes correctly.
  • Debug / flutter run → not affected (kernel ships in flutter_assets, bypassing the jniLibs chain).
  • Reproduces on two AGP/Gradle generations: the fresh flutter create template (Gradle 9.1.0 / AGP 9.0.1 / Kotlin 2.3.20) and an existing app (Gradle 8.14 / AGP 8.11.1 / Kotlin 2.2.20, Groovy DSL).
  • android.builtInKotlin=false / android.newDsl=false make no difference (tested with and without).
  • Still present in 3.44.1. Did not occur before upgrading to 3.44 (same project built correctly on the previous stable).

Why this is nasty

The build exits 0 and prints the success line, so flavored production APKs/AABs silently ship old business logic. Detection requires binary inspection — most users will misattribute it to generic caching and flutter clean it away without reporting.

Workarounds

  • flutter clean before every release build, or
  • delete build/app/intermediates/{merged_jni_libs,merged_native_libs,stripped_native_libs}/<variant> before building.

flutter doctor -v (summary)

[✓] Flutter (Channel stable, 3.44.1, on macOS 26.5 25F71 darwin-arm64)
    • Dart version 3.12.1
[✓] Android toolchain (Android SDK version 36.0.0)
    • Java OpenJDK Zulu17.54+21-CA (17.0.13+11-LTS)
[✓] Xcode 26.5

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listc: regressionIt was better in the past than it is nowhas reproducible stepsThe issue has been confirmed reproducible and is ready to work onplatform-androidAndroid applications specificallyt: gradle"flutter build" and "flutter run" on Androidteam-androidOwned by Android platform teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-androidTriaged by Android platform team

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions