Skip to content

[multi-package]Fix packages issue builtinkotlin AGP 9#11733

Open
daniJimen wants to merge 25 commits into
flutter:mainfrom
daniJimen:fix/packages_issue_builtinkotlin
Open

[multi-package]Fix packages issue builtinkotlin AGP 9#11733
daniJimen wants to merge 25 commits into
flutter:mainfrom
daniJimen:fix/packages_issue_builtinkotlin

Conversation

@daniJimen
Copy link
Copy Markdown

@daniJimen daniJimen commented May 19, 2026

Fixes flutter/flutter#186795

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or I have commented below to indicate which [test exemption] this PR falls under[^1].
  • All existing and new tests are passing.

Explanation:

Updates build.gradle.kts files across multiple Flutter plugin examples and Android library modules to conditionally apply the Kotlin Android plugin. AGP 9.0 introduces built-in Kotlin support, making explicit application of the kotlin-android plugin redundant unless specifically disabled.

Changes

  • Conditional Plugin Application: Removed the static id("kotlin-android") and added logic to check the ANDROID_GRADLE_PLUGIN_VERSION. The org.jetbrains.kotlin.android plugin is now applied programmatically only if the AGP version is below 9 or if the android.builtInKotlin Gradle property is set to false.
  • Safe Kotlin Configuration: Wrapped kotlinOptions in a conditional block. When built-in Kotlin is active (AGP 9+), the explicit kotlinOptions block is skipped. When inactive, it uses withGroovyBuilder to set jvmTarget to VERSION_17, ensuring compatibility without causing build script evaluation errors when the plugin is absent.
  • Broad Impact: Applied these changes across a wide range of packages, including camera, google_maps_flutter, image_picker, video_player, webview_flutter, shared_preferences, and pigeon.

Test plan

These changes ensure that plugin examples and libraries remain compatible across a range of AGP versions, specifically handling the transition to built-in Kotlin support in AGP 9.0 while maintaining backward compatibility for older environments.

based into: #11692

Spaccesi and others added 5 commits May 24, 2025 09:31
Pull last changes from master
## Summary

Updates the example app's `build.gradle.kts` to conditionally apply the Kotlin Android plugin based on the Android Gradle Plugin (AGP) version. AGP 9.0 introduces built-in Kotlin support, making the explicit application of the `kotlin-android` plugin redundant when enabled.

## Changes

- **Conditional Plugin Application**: Replaced the static `id("kotlin-android")` with logic that checks `ANDROID_GRADLE_PLUGIN_VERSION`. The `org.jetbrains.kotlin.android` plugin is now only applied if the AGP version is below 9 or if the `android.builtInKotlin` property is explicitly set to false.
- **Dynamic Kotlin Configuration**: Wrapped the `kotlinOptions` block in a conditional check. When built-in Kotlin is disabled, it uses `withGroovyBuilder` to set the `jvmTarget` to `VERSION_17`, preventing build script evaluation errors when the Kotlin plugin is not present.
- **AGP Property Support**: Added a check for the `android.builtInKotlin` Gradle property to allow developers to opt-out of the built-in behavior on AGP 9+.

## Test plan
This change ensures compatibility across different AGP versions used by consumers of the plugin's example application.
## Summary

Updates `build.gradle.kts` files across multiple Flutter plugin examples and Android library modules to conditionally apply the Kotlin Android plugin. AGP 9.0 introduces built-in Kotlin support, making explicit application of the `kotlin-android` plugin redundant unless specifically disabled.

## Changes

- **Conditional Plugin Application**: Removed the static `id("kotlin-android")` and added logic to check the `ANDROID_GRADLE_PLUGIN_VERSION`. The `org.jetbrains.kotlin.android` plugin is now applied programmatically only if the AGP version is below 9 or if the `android.builtInKotlin` Gradle property is set to false.
- **Safe Kotlin Configuration**: Wrapped `kotlinOptions` in a conditional block. When built-in Kotlin is active (AGP 9+), the explicit `kotlinOptions` block is skipped. When inactive, it uses `withGroovyBuilder` to set `jvmTarget` to `VERSION_17`, ensuring compatibility without causing build script evaluation errors when the plugin is absent.
- **Broad Impact**: Applied these changes across a wide range of packages, including `camera`, `google_maps_flutter`, `image_picker`, `video_player`, `webview_flutter`, `shared_preferences`, and `pigeon`.

## Test plan

These changes ensure that plugin examples and libraries remain compatible across a range of AGP versions, specifically handling the transition to built-in Kotlin support in AGP 9.0 while maintaining backward compatibility for older environments.
…iles

## Summary

Removes redundant blank lines within the conditional `isBuiltInKotlinEnabled` block across numerous package example applications. This change standardizes the formatting of the Gradle build scripts across the repository.

## Changes

- **Formatting Cleanup**: Removed extra vertical whitespace inside the `if (!isBuiltInKotlinEnabled)` check and its nested `withGroovyBuilder` block.
- **Improved Consistency**: Applied these formatting fixes to `build.gradle.kts` files in multiple packages, including `camera`, `google_maps_flutter`, `google_sign_in`, `in_app_purchase`, `video_player`, `webview_flutter`, and others.
@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 19, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

# Conflicts:
#	packages/animations/CHANGELOG.md
#	packages/camera/camera/CHANGELOG.md
#	packages/camera/camera_android_camerax/CHANGELOG.md
#	packages/espresso/CHANGELOG.md
#	packages/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md
#	packages/file_selector/file_selector/CHANGELOG.md
#	packages/file_selector/file_selector_android/CHANGELOG.md
#	packages/flutter_plugin_android_lifecycle/CHANGELOG.md
#	packages/google_fonts/CHANGELOG.md
#	packages/google_maps_flutter/google_maps_flutter_android/CHANGELOG.md
#	packages/google_sign_in/google_sign_in/CHANGELOG.md
#	packages/image_picker/image_picker/CHANGELOG.md
#	packages/image_picker/image_picker_android/CHANGELOG.md
#	packages/in_app_purchase/in_app_purchase/CHANGELOG.md
#	packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md
#	packages/interactive_media_ads/CHANGELOG.md
#	packages/local_auth/local_auth/CHANGELOG.md
#	packages/local_auth/local_auth_android/CHANGELOG.md
#	packages/path_provider/path_provider/CHANGELOG.md
#	packages/pigeon/CHANGELOG.md
#	packages/quick_actions/quick_actions/CHANGELOG.md
#	packages/quick_actions/quick_actions_android/CHANGELOG.md
#	packages/rfw/CHANGELOG.md
#	packages/shared_preferences/shared_preferences/CHANGELOG.md
#	packages/shared_preferences/shared_preferences_android/CHANGELOG.md
#	packages/two_dimensional_scrollables/CHANGELOG.md
#	packages/url_launcher/url_launcher/CHANGELOG.md
#	packages/url_launcher/url_launcher_android/CHANGELOG.md
#	packages/video_player/video_player/CHANGELOG.md
#	packages/video_player/video_player_android/CHANGELOG.md
#	packages/webview_flutter/webview_flutter/CHANGELOG.md
#	packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@daniJimen
Copy link
Copy Markdown
Author

daniJimen commented May 21, 2026

Fixes flutter/flutter#186795

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or I have commented below to indicate which [test exemption] this PR falls under[^1].
  • All existing and new tests are passing.

Explanation:

Updates build.gradle.kts files across multiple Flutter plugin examples and Android library modules to conditionally apply the Kotlin Android plugin. AGP 9.0 introduces built-in Kotlin support, making explicit application of the kotlin-android plugin redundant unless specifically disabled.

Changes

  • Conditional Plugin Application: Removed the static id("kotlin-android") and added logic to check the ANDROID_GRADLE_PLUGIN_VERSION. The org.jetbrains.kotlin.android plugin is now applied programmatically only if the AGP version is below 9 or if the android.builtInKotlin Gradle property is set to false.
  • Safe Kotlin Configuration: Wrapped kotlinOptions in a conditional block. When built-in Kotlin is active (AGP 9+), the explicit kotlinOptions block is skipped. When inactive, it uses withGroovyBuilder to set jvmTarget to VERSION_17, ensuring compatibility without causing build script evaluation errors when the plugin is absent.
  • Broad Impact: Applied these changes across a wide range of packages, including camera, google_maps_flutter, image_picker, video_player, webview_flutter, shared_preferences, and pigeon.

Test plan

These changes ensure that plugin examples and libraries remain compatible across a range of AGP versions, specifically handling the transition to built-in Kotlin support in AGP 9.0 while maintaining backward compatibility for older environments.

based into: #11692

I propose adding a CI workflow that verifies every Android-capable package in the repository builds successfully with both Android Gradle Plugin (AGP) 8.x and 9.x like:

name: Android AGP compatibility

on:
  push:
    branches: [master, main]
  pull_request:
    branches: [master, main]
  workflow_dispatch:

permissions:
  contents: read

jobs:
  discover-examples:
    runs-on: ubuntu-latest
    outputs:
      examples: ${{ steps.find.outputs.examples }}
    steps:
      - uses: actions/checkout@v6

      - name: Discover Android examples
        id: find
        run: |
          # Find all example/android/settings.gradle.kts across packages/ and third_party/packages/
          examples=$(find packages third_party/packages -path "*/example/android/settings.gradle.kts" \
            -not -path "*/build/*" \
            2>/dev/null \
            | sed 's|/android/settings.gradle.kts||' \
            | sort \
            | jq -R -s -c 'split("\n") | map(select(length > 0))')
          echo "examples=$examples" >> "$GITHUB_OUTPUT"
          echo "Found examples:"
          echo "$examples" | jq .

  android-agp-matrix:
    needs: discover-examples
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        example: ${{ fromJson(needs.discover-examples.outputs.examples) }}
        agp:
          - lane: agp-8.x
            agp_version: "8.9.1"
            gradle_version: "8.14.3"
            kotlin_version: "2.2.20"
            built_in_kotlin: ""
          - lane: agp-9.x
            agp_version: "9.2.1"
            gradle_version: "9.5.0"
            kotlin_version: "2.3.21"
            built_in_kotlin: "false"

    name: "${{ matrix.example }} — ${{ matrix.agp.lane }}"

    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '17'

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: 3.32.8
          cache: true

      - name: Show toolchain versions
        run: |
          flutter --version
          java -version

      - name: Patch Android config for ${{ matrix.agp.lane }}
        shell: bash
        env:
          AGP_VERSION: ${{ matrix.agp.agp_version }}
          GRADLE_VERSION: ${{ matrix.agp.gradle_version }}
          KOTLIN_VERSION: ${{ matrix.agp.kotlin_version }}
          BUILT_IN_KOTLIN: ${{ matrix.agp.built_in_kotlin }}
          EXAMPLE_DIR: ${{ matrix.example }}
        run: |
          python3 <<'PY'
          from pathlib import Path
          import os
          import re
          import sys

          agp_version = os.environ['AGP_VERSION']
          gradle_version = os.environ['GRADLE_VERSION']
          kotlin_version = os.environ['KOTLIN_VERSION']
          built_in_kotlin = os.environ['BUILT_IN_KOTLIN']

          example_dir = Path(os.environ['EXAMPLE_DIR'])

          def patch_settings(settings_file):
              """Patch AGP and Kotlin versions in a settings.gradle.kts file."""
              if not settings_file.exists():
                  return

              settings = settings_file.read_text()

              # Patch AGP (com.android.application or com.android.library)
              settings, n = re.subn(
                  r'id\("com\.android\.(application|library)"\) version "[^"]+" apply false',
                  lambda m: f'id("com.android.{m.group(1)}") version "{agp_version}" apply false',
                  settings,
              )
              if n == 0:
                  print(f"⚠️  No AGP plugin found in {settings_file}, skipping AGP patch.")

              # Patch Kotlin version
              settings, n = re.subn(
                  r'id\("org\.jetbrains\.kotlin\.android"\) version "[^"]+" apply false',
                  f'id("org.jetbrains.kotlin.android") version "{kotlin_version}" apply false',
                  settings,
              )
              if n == 0:
                  print(f"⚠️  No Kotlin plugin found in {settings_file}, skipping Kotlin patch.")

              settings_file.write_text(settings)
              print(f"✅ Patched {settings_file}")

          def patch_wrapper(wrapper_file):
              """Patch Gradle distribution version."""
              if not wrapper_file.exists():
                  return
              wrapper = wrapper_file.read_text()
              wrapper, n = re.subn(
                  r'gradle-[^-]+-all\.zip',
                  f'gradle-{gradle_version}-all.zip',
                  wrapper, count=1,
              )
              if n != 1:
                  print(f"⚠️  Could not patch Gradle wrapper in {wrapper_file}")
                  sys.exit(1)
              wrapper_file.write_text(wrapper)
              print(f"✅ Patched {wrapper_file}")

          def patch_gradle_properties(props_file):
              """Patch builtInKotlin flag."""
              if not props_file.exists():
                  return
              filtered_lines = []
              for line in props_file.read_text().splitlines():
                  if line.startswith('android.builtInKotlin='):
                      continue
                  if line.strip() == '# This builtInKotlin flag was added automatically by Flutter migrator':
                      continue
                  filtered_lines.append(line)
              if built_in_kotlin:
                  filtered_lines.append(f'android.builtInKotlin={built_in_kotlin}')
              props_file.write_text('\n'.join(filtered_lines) + '\n')
              print(f"✅ Patched {props_file}")

          # 1. Patch the example's android/ directory
          example_android = example_dir / 'android'
          if not (example_android / 'settings.gradle.kts').exists():
              print(f"⚠️  {example_android / 'settings.gradle.kts'} not found, skipping.")
              sys.exit(0)

          patch_settings(example_android / 'settings.gradle.kts')
          patch_wrapper(example_android / 'gradle' / 'wrapper' / 'gradle-wrapper.properties')
          patch_gradle_properties(example_android / 'gradle.properties')

          # 2. Patch the parent package's android/ directory (plugin library project)
          #    The package root is the parent of the example/ directory.
          package_android = example_dir.parent / 'android'
          if (package_android / 'settings.gradle.kts').exists():
              print(f"📦 Also patching plugin package at {package_android}")
              patch_settings(package_android / 'settings.gradle.kts')
              patch_wrapper(package_android / 'gradle' / 'wrapper' / 'gradle-wrapper.properties')
              patch_gradle_properties(package_android / 'gradle.properties')

          print("🎉 All patching complete.")
          PY

      - name: Show Android diff
        run: |
          # Show diff for the example's android dir
          git --no-pager diff -- "${{ matrix.example }}/android" | cat
          # Show diff for the parent package's android dir (if it exists)
          PACKAGE_ANDROID="$(dirname "${{ matrix.example }}")/android"
          if [ -d "$PACKAGE_ANDROID" ]; then
            git --no-pager diff -- "$PACKAGE_ANDROID" | cat
          fi

      - name: Install dependencies
        working-directory: ${{ matrix.example }}
        run: flutter pub get

      - name: Build Android APK (debug)
        working-directory: ${{ matrix.example }}
        run: flutter build apk --debug

What do you think? I appreciate your opinion. @reidbaker @jesswrd

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AGP 9+: Unconditional Kotlin plugin/kotlin {} usage causes build failures in package Gradle scripts

3 participants