Skip to content

[DO NOT MERGE] Add AGP 9 compatibility#1765

Open
ajpallares wants to merge 14 commits into
mainfrom
pallares/agp9-kotlin-conditional
Open

[DO NOT MERGE] Add AGP 9 compatibility#1765
ajpallares wants to merge 14 commits into
mainfrom
pallares/agp9-kotlin-conditional

Conversation

@ajpallares
Copy link
Copy Markdown
Member

@ajpallares ajpallares commented May 29, 2026

Motivation

Resolves #1759. AGP 9 turns our current Flutter warning into a build failure. Migration follows Flutter's plugin author guide.

Summary

  • Main package's Android sources are Java-only, so KGP is removed.
  • UI package applies kotlin-android only for AGP < 9; AGP ≥ 9 uses built-in Kotlin.
  • Both kotlinOptions (legacy) and kotlin.compilerOptions (new) DSLs are gated on the same check.
  • New agp9-smoke-test CI job evaluates the UI package's build.gradle under AGP 9 to catch regressions on the new branch.

Notes

The Flutter guide unconditionally uses kotlin.compilerOptions, which breaks consumers pinning KGP 1.x via settings.gradle (the Flutter 3.22 default). Both DSLs are therefore gated.


Note

Medium Risk
Changes Android Gradle/Kotlin wiring for both plugins, which can break consumer builds on AGP 8 vs 9; mitigated by dual-path logic and a dedicated AGP 9 smoke job, but full Kotlin compile under AGP 9 is not exercised in CI.

Overview
This PR prepares the Flutter Android plugins for AGP 9 and Flutter’s built-in Kotlin migration.

The core purchases_flutter Android module drops Kotlin Gradle Plugin usage entirely (no kotlin-android, kotlinOptions, or kotlin-stdlib), since that package’s Android code is Java-only.

purchases_ui_flutter keeps Kotlin but branches on detected AGP major version and android.builtInKotlin: for AGP < 9 (or AGP 9 with built-in Kotlin disabled), it still applies kotlin-android, adds the KGP classpath, and sets JVM target via android.kotlinOptions. For AGP ≥ 9 with built-in Kotlin enabled, it skips the legacy plugin and uses top-level kotlin { compilerOptions { … } } instead—avoiding breakage for apps that still pin KGP 1.x.

CI adds an agp9-smoke-test job (JDK 21 / Gradle 9.1 via .circleci/agp9-smoke) that includes the UI plugin under AGP 9.0.1 and asserts agpMajor == 9 plus that compileDebugKotlin resolves with --dry-run—configuration-time coverage only, not a full compile.

Reviewed by Cursor Bugbot for commit 1939595. Bugbot is set up for automated code reviews on this repo. Configure here.

The main plugin's Android sources are Java-only, so KGP is dropped
entirely there.

The UI plugin still needs Kotlin, so kotlin-android is now applied
only when AGP < 9 (AGP 9+ provides built-in Kotlin via KGP 2.2.10+).
Because consumers on Flutter 3.22 may still pin KGP 1.x, both the
legacy android.kotlinOptions and the new top-level
kotlin.compilerOptions DSLs are gated on the same AGP version check.

Resolves #1759.

Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares added the pr:feat A new feature label May 29, 2026
@ajpallares ajpallares changed the title Apply kotlin-android conditionally for AGP 9 compatibility Add AGP 9 compatibility May 29, 2026
ajpallares and others added 9 commits May 29, 2026 11:12
No functional change: configures kotlinOptions / kotlin.compilerOptions
in one place instead of two separate conditional blocks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Under AGP 9+ Kotlin is provided by built-in Kotlin (KGP 2.2.10+),
so the kotlin-gradle-plugin classpath we declare for AGP < 9 is
redundant and could in theory cause classpath conflicts. Gate it
on the same agpMajor check as the plugin application.

agpMajor is now defined as ext.agpMajor inside the buildscript
block (rather than at the script top level) because Gradle
processes the buildscript block before the rest of the script
body. Defining it via ext makes it accessible everywhere.

Co-authored-by: Cursor <cursoragent@cursor.com>
KGP 2.0+ is the actual floor required for the top-level
kotlin.compilerOptions DSL (per the Flutter migration guide),
and matches what's documented. The previous "2.2.10+" was an
implementation detail of what AGP 9.0 currently bundles, which
isn't a useful version to pin in a comment.

Co-authored-by: Cursor <cursoragent@cursor.com>
The plugin's android/build.gradle has an AGP-version conditional that
switches to a new top-level kotlin.compilerOptions DSL when running
under AGP 9 (which provides Kotlin built-in via KGP 2.x). That branch
is not exercised by any existing build, because the example apps and
CI all run under AGP 8 + KGP 1.x and the Flutter ecosystem isn't yet
fully AGP 9-ready end to end.

This adds a small Gradle-only fixture under .circleci/agp9-smoke/ that
pins AGP 9.0.1 and KGP 2.2.10 via pluginManagement and runs the plugin
in isolation. It's a configuration-time smoke test: it asserts that
agpMajor evaluates to 9 (so the AGP-9 branch ran) and that the
compileDebugKotlin task graph resolves under --dry-run (so AGP applied
cleanly and the new DSL is valid). It does not run a full build.

A new agp9-smoke-test CircleCI job wires this into the test workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
Drop redundant qualifiers; the surrounding sentences already explain
what the smoke test does and doesn't cover.

Co-authored-by: Cursor <cursoragent@cursor.com>
The previous version of the job installed SDKMAN with an unpinned
'curl | bash' and downloaded a Gradle distribution from gradle.org
inline. Both of those are now handled by the shared infrastructure:

- revenuecat/install-sdkman from the shared orb installs SDKMAN
  with a pinned, SHA-verified release artifact.
- A local .sdkmanrc next to the smoke test pins both candidates
  (java=21.0.7-librca, gradle=9.1.0). `sdk env install` reads it.

The local `install-sdkman` command in config.yml is bypassed because
it auto-runs `sdk env install` against the project root's .sdkmanrc,
which pins JDK 17.

Co-authored-by: Cursor <cursoragent@cursor.com>
The previous version of the install step pinned versions in both
.circleci/agp9-smoke/.sdkmanrc (consumed by `sdk env install`) and
the install command itself (used by `sdk home` to compute paths).
Parse the versions out of .sdkmanrc instead, so the file is the
single source of truth.

Co-authored-by: Cursor <cursoragent@cursor.com>
The orb's install-sdkman step already sources sdkman-init.sh in
$BASH_ENV for every subsequent step. sdkman-init.sh reads the
candidate `current` symlinks at every shell startup and sets
JAVA_HOME and PATH from them. In a fresh SDKMAN install, the first
install of each candidate is auto-set as its default, so after
`sdk env install` runs, the orb's existing $BASH_ENV plumbing
takes care of activating the right versions in every later step.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
ajpallares and others added 3 commits June 1, 2026 12:57
The earlier gating only checked AGP major version, which assumed that on
AGP 9+ the kotlin {} extension is always registered (either by
kotlin-android or by AGP's built-in Kotlin). That assumption breaks when
the consumer sets android.builtInKotlin=false in gradle.properties — the
default the Flutter migrator adds for Flutter 3.44+ apps. In that
configuration neither path registers the kotlin {} extension, and our
top-level kotlin { compilerOptions { ... } } block fails at configuration
time with `Could not find method kotlin()`.

Apply kotlin-android (and pull its classpath) whenever AGP won't provide
Kotlin itself: AGP < 9, or AGP 9+ with built-in Kotlin opted out.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ajpallares ajpallares marked this pull request as ready for review June 1, 2026 11:20
@ajpallares ajpallares requested a review from a team as a code owner June 1, 2026 11:20
@ajpallares ajpallares changed the title Add AGP 9 compatibility [DO NOT MERGE] Add AGP 9 compatibility Jun 1, 2026
@ajpallares
Copy link
Copy Markdown
Member Author

We've decided to hold off on the migration until the issues we have found are solved in Flutter (more context here)

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

Labels

pr:feat A new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Please migrate your plugin to Built-in Kotlin (for AGP9)

1 participant