Repository navigation
Android plugins apply KGP based on AGP major version only, breaking on AGP 9 with android.builtInKotlin=false #3944
Description
Activity
Pull requests, one per package as CONTRIBUTING requires:
android_alarm_manager_plus— fix(android_alarm_manager_plus): apply KGP unless built-in Kotlin is enabled #3945battery_plus— fix(battery_plus): apply KGP unless built-in Kotlin is enabled #3946device_info_plus— fix(device_info_plus): apply KGP unless built-in Kotlin is enabled #3947network_info_plus— fix(network_info_plus): apply KGP unless built-in Kotlin is enabled #3948package_info_plus— fix(package_info_plus): apply KGP unless built-in Kotlin is enabled #3949sensors_plus— fix(sensors_plus): apply KGP unless built-in Kotlin is enabled #3950share_plus— fix(share_plus): apply KGP unless built-in Kotlin is enabled #3951
Each carries the identical one-file change and is independently mergeable; none of them closes this issue on merge, so it can be closed once all seven land (or repurposed if a different shape is preferred).
Upgrade this alone we are facing the issues
Dependency 'androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0' requires libraries and applications that depend on it to compile against version 34 or later of the Android APIs. :network_info_plus is currently compiled against android-33. Recommended action: Update this project to use a newer compileSdk of at least 34, for example 36. Note that updating a library or application's compileSdk (which allows newer APIs to be used) can be done separately from updating targetSdk (which opts the app in to new runtime behavior) and minSdk (which determines which devices the app can be installed on).We hit this on AGP 9.0.1 with
android.builtInKotlin=falseand can add two
symptoms that differ from theKotlinAndroidProjectExtension does not existerror in
the report. On our setup configuration succeeds and the failure lands later, during
compilation — so this may be worth listing as additional signatures of the same root
cause.Environment: Flutter 3.47.2, AGP 9.0.1, Gradle 9.1.0, Kotlin 2.3.20, Gradle JDK 21,
compileSdk/targetSdk36,android.builtInKotlin=false,android.newDsl=false.Symptom A — plugin classes never reach the library's compile jar
device_info_plus13.2.0 andpackage_info_plus10.2.1,flutter build apk --release::app:compileDevReleaseJavaWithJavac GeneratedPluginRegistrant.java:39: error: cannot find symbol flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.device_info.DeviceInfoPlusPlugin()); ^ symbol: class DeviceInfoPlusPlugin location: package dev.fluttercommunity.plus.device_info GeneratedPluginRegistrant.java:159: error: cannot find symbol flutterEngine.getPlugins().add(new dev.fluttercommunity.plus.packageinfo.PackageInfoPlugin()); ^ symbol: class PackageInfoPlugin location: package dev.fluttercommunity.plus.packageinfo 2 errorsThe likely useful detail: the Kotlin compilation itself succeeds. Both of these
pass when invoked directly, and the task does exist —./gradlew :device_info_plus:tasks --all | grep compileReleaseKotlin # present ./gradlew :device_info_plus:compileReleaseKotlin # rc=0 ./gradlew :package_info_plus:compileReleaseKotlin # rc=0So classes are produced but never land in the module's
compile_library_classes_jar,
and consumers cannot resolve them.Symptom B — share_plus additionally loses its
src/main/kotlinsourcesshare_plus13.3.0 does not compile at all:e: .../share_plus-13.3.0/android/src/main/kotlin/dev/fluttercommunity/plus/share/Share.kt:185:41 Unresolved reference 'SharePlusPendingIntent'. e: .../Share.kt:185:71 Cannot infer type for type parameter 'T'. Specify it explicitly.SharePlusPendingIntentis declared in the same package, in
android/src/main/kotlin/dev/fluttercommunity/plus/share/SharePlusPendingIntent.kt.
build.gradle.ktshas nosourceSetsblock registeringsrc/main/kotlin, which KGP
used to contribute implicitly — so alongside the property check,share_pluslooks
like it also needs that registration added back.Bisected:
share_plus13.0.0 builds fine (it still applieskotlin-android
unconditionally); 13.3.0 fails as above.Two notes that may help narrow it down
- The report lists
sensors_plusas affected. On our setup (AGP 9.0.1)
sensors_plusandflutter_email_sender_method_channelbuild fine, while
device_info_plus,package_info_plusandshare_plusfail — even though all five
use the AGP-version-only check and all five configure
KotlinAndroidProjectExtension. We could not work out what distinguishes them; given
the report used AGP 9.1.0, an AGP patch-version difference may be involved. - These three are awkward to test in isolation because
win32 ^6couples them:
package_info_plus≥10.1.0 requireswin32 ^6.0.1, which forcesshare_plus
≥13.0.0 anddevice_info_plus≥13.2.0. We could only observe them together, so
please verify each package separately rather than trusting our grouping.
This also blocks upgrading
file_pickerto 12.x, since that upgrade pulls the same
win32 ^6cluster.- The report lists
In case it is not clear from the Summary of ticket, what this causes is that if we have one of these packages as a dependency, when we try to migrate to AGP >= 9, we need to be sure that all dependencies also support built-in kotlin, otherwise the project will not build for android, and this makes it very hard to move to AGP 9 (which makes it important since flutter will eventually drop support to AGP < 9)
I just had time to rebuild one of my apps, and I can't reproduce the issue described here.
My environment is:
- Flutter 3.47
- AGP 9.2.1
- Gradle 9.5
with the following
gradle.properties# other settings ... # This newDsl flag was added automatically by Flutter migrator android.newDsl=false # This builtInKotlin flag was added automatically by Flutter migrator android.builtInKotlin=falseI'm using
share_plusandpackage_info_plus, and the app bundle builds successfully with the following warnings:Running Gradle task 'bundleRelease'... WARNING: Your app uses the following plugins that apply Kotlin Gradle Plugin (KGP): firebase_analytics, firebase_app_check, firebase_core, firebase_crashlytics Future versions of Flutter will fail to build if your app uses plugins that apply KGP.Would it be possible to reproduce the issue using one of the demo/example apps? That might make it easier for the maintainers to reproduce and confirm the problem.
Just to be sure, did you set
android.builtInKotlin=false?Right, I just added my
gradle.propertiesabove.I didn't do much before running
flutter build appbundle, other than:- upgrading Flutter from 3.44 to 3.47, and
- upgrading all dependencies.
Cross-reference: I left a longer report on #3949 (the
package_info_pluspatch), rather
than duplicate it here — #3949 (comment)The short version, on Flutter 3.47.0 / AGP 9.0.1 / Gradle 9.1.0: the workaround usually
suggested for this issue,android.builtInKotlin=true, is not available on that Flutter
version. AGP's built-in Kotlin pins KGP 2.2.10 (same pin in the AGP POMs from 9.0.1
through 9.4.1) and Flutter refuses to build below 2.2.20, so both positions of the
switch fail. Downgradingpackage_info_plusto 9.0.1 does not help either, because
file_picker≥ 12 — the first version that handles AGP 9 correctly — requires
package_info_plus: ^10.2.1.Also worth noting for whoever picks this up: Flutter's own KGP fallback
(FlutterPluginUtils.detectApplyingKotlinGradlePlugin) does not cover these modules. It
decides by looking for the plugin declaration in the build file, and the
apply(plugin = "org.jetbrains.kotlin.android")is present — inside the dead
if (agpMajor < 9)branch — so it concludes KGP is already applied and skips the module.The seven PRs from 2026-07-31 are all still open and unreviewed.
I had the same
cannot find symbolerror. Switching from Gradle 9.4 to 9.5 solved the issue for me. Using Flutter 3.44 and AGP 9.2.1.
Summary
Seven Android plugins decide whether to apply the Kotlin Gradle Plugin (KGP) based on the AGP major version alone:
This assumes AGP 9 always means built-in Kotlin is active. That is not true. AGP 9 enables built-in Kotlin only when
android.builtInKotlinis not explicitlyfalse— andandroid.builtInKotlin=falseis exactly whatflutter createwrites into every new app'sandroid/gradle.properties(via the Flutter migrator), and what every example app in this repository currently sets.So on AGP 9 +
android.builtInKotlin=falsethe guard isfalse, the plugin does not apply KGP, and AGP does not provide Kotlin either. Nothing compiles the plugin's Kotlin sources, and configuration fails a few lines below at the unconditional extension lookup:Why this is not visible yet
Flutter's Gradle plugin applies
kotlin-androidon behalf of any Android subproject whose build script does not appear to declare KGP. It decides that by regex over the build file text, and the Kotlin-DSL regex (FlutterPluginUtils.kgpRegexKotlin) only matchesplugins { }blocks:It does not match the imperative
apply(plugin = "…")form these plugins use. So Flutter does not see the declaration, applies KGP itself, and accidentally papers over the broken guard.Note the asymmetry: the Groovy counterpart
kgpRegexGroovydoes matchapply plugin: '…'. That is precisely why this bug has already been hit in a Groovy-based plugin but not here — see the prior art below.This is load-bearing accident, not a contract. Flutter's own tooling already warns that this fallback is going away:
Reproduction
Verified on AGP 9.1.0 / Kotlin 2.4.0 / Gradle 9.3.1, Flutter 3.44.8 stable, with
android.builtInKotlin=false.Because Flutter's fallback currently masks the bug, the decisive reproduction removes that mask. In a local Flutter SDK checkout, extend
kgpRegexKotlininpackages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.ktso it also matches the imperative form, by prefixing this alternative to the existing pattern:Then, in any of the affected packages' examples, bump the example to AGP 9 and run
flutter build apk --debugwithandroid.builtInKotlin=false. The build fails with theKotlinAndroidProjectExtension does not existerror above. Reverting the SDK edit makes it build again — confirming the plugin currently depends on Flutter's fallback rather than on its own guard.Evidence that the condition is wrong, not just unlucky
android.builtInKotlindefaults to enabled in AGP 9. Fromcom/android/build/gradle/options/BooleanOptioningradle-9.1.0.jar:So: absent ⇒ built-in Kotlin on; explicit
false⇒ off. The version check alone cannot tell those apart. (It is alsoSoftlyEnforcedwith removal targeted at AGP 10, so opting out is temporary.)Proposed fix
Guard on the same condition AGP itself uses — built-in Kotlin is on when AGP >= 9 and
android.builtInKotlinis not explicitlyfalse:The
project.extensions.configure(KotlinAndroidProjectExtension::class.java) { … }block below stays unchanged: AGP 9's built-in Kotlin registers that extension, so it resolves under both branches (confirmed on a real AGP 9.1 build withandroid.builtInKotlin=true).On AGP 8 the behaviour is provably identical to today:
agpMajor >= 9is false, sobuiltInKotlinEnabledis false and KGP is applied exactly as before.Note on prior art
app_settings8.0.3 hit this exact failure and fixed it (spencerccf/app_settings#270); itsandroid/build.gradlecarries a long comment documenting the mechanism. Its fix is not worth copying verbatim: it reads the property with a?: 'false'default and ignores the AGP version, so on an AGP 9 project that never sets the property it applies KGP into a build where built-in Kotlin is already active, which throws:Flutter apps always write the property, so that case is masked there — but the version above is correct in both cases.
Affected packages
android_alarm_manager_plusbattery_plusdevice_info_plusnetwork_info_pluspackage_info_plussensors_plusshare_plusandroid_intent_plususes a Groovybuild.gradlewith no Kotlin sources and is not affected.Verification performed
builtInKotlin=false, before fixKotlinAndroidProjectExtension does not existbuiltInKotlin=false, after fixbuiltInKotlin=trueAdditionally, after the fix, Flutter's warning lists the plugin under "plugins that apply KGP", which means the patched regex did detect the declaration and Flutter did not apply KGP as a fallback — the plugin stands on its own.
As a control, the fix was reverted in a single package (
share_plus) and the AGP 9 build failed identically, confirming the passing results are meaningful rather than vacuous.Per CONTRIBUTING, this is filed as one issue with a proposal, with one branch and pull request per package to follow.