Skip to content

Migrate off legacy Kotlin Gradle Plugin apply per the built-in-Kotlin migration (#636) - #643

Open
aki1770-del wants to merge 1 commit into
dlutton:masterfrom
aki1770-del:fix/kotlin-gradle-plugin-migration
Open

Migrate off legacy Kotlin Gradle Plugin apply per the built-in-Kotlin migration (#636)#643
aki1770-del wants to merge 1 commit into
dlutton:masterfrom
aki1770-del:fix/kotlin-gradle-plugin-migration

Conversation

@aki1770-del

Copy link
Copy Markdown

Addresses #636. Applies the official plugin-author migration (docs.flutter.dev → "Migrate to built-in Kotlin" → plugin authors), using the documented variant for plugins that support Flutter versions earlier than 3.44: KGP is applied conditionally only while AGP < 9, and android.kotlinOptions moves to the kotlin { compilerOptions { jvmTarget = ... } } DSL. The buildscript Kotlin classpath is retained because the conditional branch can still apply KGP on older toolchains (kotlin_version 2.2.20 satisfies the compilerOptions DSL floor).

Verification (build-verified locally before submitting): flutter build apk --debug on the example is green on a current pre-release SDK (post-deprecation Gradle semantics, so the new DSL is exercised at configure time) and on Flutter 3.38.3 (older-consumer back-compat).

Note: I couldn't capture the warning-disappears transition the 3.44 reporters see — the SDKs I have locally don't emit the KGP deprecation warning even on unmigrated master. This change follows the official migration doc for plugins supporting pre-3.44 Flutter, and the example builds green on both a current pre-release SDK and 3.38.3. Happy to adjust if CI on a newer stable shows anything different.

…lutton#636)

Flutter 3.44+ deprecates plugins that apply KGP themselves, and AGP 9
removes support for it (built-in Kotlin). Per the official plugin-author
migration guide
(https://docs.flutter.dev/release/breaking-changes/migrate-to-built-in-kotlin/for-plugin-authors),
apply kotlin-android only when the consuming app's AGP major version is
below 9, and replace the android.kotlinOptions block with the
kotlin.compilerOptions DSL (requires KGP >= 2.0, already satisfied by
kotlin_version 2.2.20). The conditional keeps the plugin buildable for
apps on older stable Flutter/AGP versions.
@Wiserjr

Wiserjr commented Aug 19, 2026

Copy link
Copy Markdown

I tested this patch against a real app on AGP 9 — build and runtime — and wanted
to share the results, since they are mixed in a way that might help the review.

Environment

Flutter 3.47.0 (stable)
AGP 9.1.0
Kotlin 2.4.0
Gradle 9.3.1
flutter_tts 4.2.5
compileSdk / minSdk 36 / 24

Baseline — 4.2.5 unpatched, android.builtInKotlin=true

The build fails, exactly as the migration guide predicts:

FAILURE: Build completed with 2 failures.

* What went wrong:
> Failed to apply plugin 'kotlin-android'.
   > Failed to apply plugin 'org.jetbrains.kotlin.android'
     The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin
     support since AGP 9.0.
     Solution: Remove the 'org.jetbrains.kotlin.android' plugin from this
     project's build file:
     ...\Pub\Cache\hosted\pub.dev\flutter_tts-4.2.5\android\build.gradle

With this PR applied, android.builtInKotlin=true

The build succeeds:

√ Built build\app\outputs\flutter-apk\app-debug.apk

So the patch does fix the actual breakage under built-in Kotlin. 👍

However, the Flutter warning is still emitted

WARNING: Your app uses the following plugins that apply Kotlin Gradle Plugin
(KGP): flutter_tts
Future versions of Flutter will fail to build if your app uses plugins that
apply KGP.

My guess is that Flutter's check is a static scan of the plugin's
android/build.gradle rather than an inspection of what actually got applied,
so it still matches on the two remaining occurrences:

  • the buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } block, and
  • the literal apply plugin: 'kotlin-android' string, even though it now sits
    inside if (agpMajor < 9).

If that's right, fully clearing the warning would also mean making the
buildscript classpath conditional (or dropping it, if the plugin no longer
needs to resolve KGP itself on AGP 9+). Happy to test any follow-up commit on
the same setup.

Runtime check

I also installed the patched release build on an emulator (Android 16, x86_64,
Google TTS engine) and exercised the plugin end to end — speak() on a series
of utterances, with the app's UI following the progress handler:

GoogleTTSServiceImpl: currentLocale = pt-BR
GoogleTTSServiceImpl: For default lang pt-br is name pt-BR-language
PipelineReaderImpl: Initializing monolithic pipeline from disk:
  .../superpacks/pt-br/pt-br-x-multi-seanet-r64/pipeline.pb
AudioTrack: stop(30): called with 802170 frames delivered

Audio was actually rendered, and the progress callbacks fired continuously —
the app highlights the utterance being spoken, and the highlight advanced
through the list as playback went on. So initialization, engine/voice
selection, synthesis, playback and the progress handler all work under built-in
Kotlin with this patch.

Either way, the functional fix here is the part that matters for AGP 9 users
today, and it works. Thanks for putting it together.

@aki1770-del

Copy link
Copy Markdown
Author

Thank you for this — and especially for the runtime pass. Nobody had verified this end to end on a device before you did.

Before the warning, though: there is a configuration you have not tested that I think may be broken by this PR, and you are the only person with a rig that can check it.

You tested android.builtInKotlin=true. On AGP 9 with android.builtInKotlin=false, I think this plugin may fail to configure. The chain, from FlutterPluginUtils.kt:

  • our guard is if (agpMajor < 9), so on AGP 9 we do not apply KGP;
  • Flutter's own fallback only applies it when !isBuiltInKotlinEnabled && !hasKgpPlugin, and hasKgpPlugin is true for us — the regex matched our guarded line — so Flutter does not apply it either;
  • nothing has applied KGP, and the file still declares kotlin { compilerOptions { … } }.

flutter/flutter#190339 is where I got this. Its author hit that failure on purpose: he tried making kgpRegexKotlin match the imperative Kotlin form, ran it against device_info_plus 13.2.0 (which guards the same way), and got Extension of type 'KotlinAndroidProjectExtension' does not exist on AGP 9 with builtInKotlin=false. He concluded under-matching was the safe direction and left the Kotlin regex alone. The catch is that kgpRegexGroovy already matches the guarded Groovy form — so what he engineered around for Kotlin DSL looks like stock behaviour for Groovy plugins like this one.

I have not been able to run it: no AGP 9 rig here. So that is a reading of the source, not a result, and I could be wrong — I searched flutter/flutter for that error string and found only #190339 itself, no in-the-wild report. But if it does fail for you, it is worth knowing before this merges, and it would be a real bug in the patch rather than a cosmetic warning.

On the warning itself — you were right about the mechanism. FlutterPluginUtils.getSubprojectPluginState says so in its own doc comment: it "inspects build script files directly via regex rather than querying Gradle plugin state at runtime", because a runtime-conditional apply causes lifecycle ordering problems (gradle#36953). And kgpRegexGroovy is anchored ^[ \t]*apply[ \t]+plugin…, so the guarded line still matches — indenting it inside if (agpMajor < 9) hides nothing.

One correction, and it saves you a build: the buildscript classpath is not part of it. None of the regexes read buildscript or classpath — they match only apply plugin: and plugins { id/alias }. I ran the tool's own regexes against this PR's android/build.gradle with that line removed, and the match is unchanged. Making it conditional would not clear the warning.

I do not think there is a follow-up commit worth your time here. This file is already the migration guide's own "Support Flutter versions earlier than 3.44" snippet, verbatim — and that snippet matches the regex too. It is a false positive on AGP 9+, where the guard is false and KGP is never applied. It is already filed: flutter/flutter#189770 (P2, triaged; cdeil confirmed it on 3.47.0 / AGP 9.1.0 using the guide's snippet on a freshly generated plugin), and #190339 is a written, tested fix that rebuilds the report from PluginManager.hasPlugin instead of file text — currently waiting on a CLA signature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants