跪拜 Guibai
← All articles
Flutter · Dart · Android

Keep Flutter Plugins Alive on Old and New AGP Without Breaking Changes

By GitLqr ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Plugin maintainers who ship libraries used across a wide Flutter version range face a real split: either drop legacy users or risk build crashes. This technique keeps the install base intact while satisfying the new AGP constraints, and it exposes how Flutter's own tooling scans for KGP usage — a detail that can trip up any Android plugin author.

Summary

Starting with Flutter 3.44 and AGP 9.0, Kotlin support is built into the Android Gradle Plugin. Any plugin that still applies `kotlin-android` directly will trigger build warnings now and hard failures later. The official migration path demands a minimum Flutter version bump to 3.44, which abandons users on older releases. A conditional Gradle configuration sidesteps this trade-off by checking the AGP major version and the `android.builtInKotlin` property at configuration time. It applies the Kotlin plugin only when the built-in support is absent, and it selects the correct JVM target DSL based on whether the Kotlin extension exposes `compilerOptions`. String concatenation tricks defeat Flutter CLI's static regex scanner, which otherwise flags any literal `apply plugin: "kotlin-android"` even inside a dead branch. The result is a single plugin codebase that compiles cleanly from Flutter 3.0 through 3.44 and beyond, with no breaking version bump and no manual `gradle.properties` workarounds required from consumers.

Takeaways
AGP 9.0 bakes Kotlin support directly into the Android Gradle Plugin, making explicit `apply plugin: 'kotlin-android'` in library plugins a future build-breaking conflict.
Flutter's CLI uses static regex to detect KGP usage in `build.gradle` files; it does not evaluate Groovy conditionals, so even dead code triggers the warning.
Reading `com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION` at configuration time gives the AGP major version, which is the key to branching logic.
Checking the `android.builtInKotlin` project property alongside the AGP version reliably determines whether built-in Kotlin is active.
Applying the Kotlin plugin via string concatenation (`"kotlin-" + "android"`) hides the literal from Flutter's regex scanner while preserving correct behavior.
JVM target configuration requires a runtime property check: use `compilerOptions` when the Kotlin extension exposes it, otherwise fall back to `kotlinOptions`.
Consumers need no `gradle.properties` changes; the compatibility logic lives entirely inside the plugin's own build script.
Conclusions

The Flutter toolchain's reliance on static regex scanning rather than AST or runtime evaluation creates a cat-and-mouse game where plugin authors must obfuscate valid Groovy to pass a lint check.

AGP's move to built-in Kotlin simplifies the top-level app project but pushes complexity onto library maintainers, who now carry version-detection logic that the platform itself could provide through a standard Gradle property.

The two JVM target DSLs — `kotlinOptions` and `compilerOptions` — coexist awkwardly because Kotlin Gradle Plugin 1.9+ changed the API without a clean compatibility shim, forcing every multi-target plugin to write its own feature detection.

Concepts & terms
Built-in Kotlin (AGP 9.0+)
Starting with Android Gradle Plugin 9.0, Kotlin support is included natively in AGP. Plugins no longer need to declare `kotlin-android` or add KGP to `buildscript.dependencies`; doing so conflicts with the built-in mechanism.
Flutter CLI static regex KGP detection
Flutter's build tooling scans plugin `build.gradle` files with regular expressions for literal strings like `apply plugin: "kotlin-android"`. It does not parse Groovy conditionals, so even code inside an `if (false)` block triggers the deprecation warning.
Kotlin JVM target DSL split
Kotlin Gradle Plugin 1.9+ introduced a new `kotlin.compilerOptions.jvmTarget` DSL. Older versions use `android.kotlinOptions.jvmTarget`. A plugin targeting both must detect which API is available at configuration time to avoid build errors.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗