Start Debugging
2026-09-18 Updated 2026-09-18 migrationflutterandroidgradle Edit on GitHub

Migrate a Flutter Android project to AGP 9 with built-in Kotlin

The full path from an AGP 8 Flutter app that applies kotlin-android to AGP 9.1 with android.builtInKotlin=true on Flutter 3.47. Every step was built and measured, including the two edits that look optional and are not: kotlinOptions is a compile error under KGP 2.2+, and the KGP line in settings.gradle.kts has to stay.

For a Flutter app created before Flutter 3.44, the migration is four edits: bump the Gradle wrapper to 9.3.1 and AGP to 9.1.0, bump the Kotlin Gradle Plugin (KGP) version in settings.gradle.kts to 2.4.0 but keep the line, replace kotlinOptions with a top-level kotlin { compilerOptions { ... } } block, and delete id("kotlin-android") from app/build.gradle.kts. Then flip android.builtInKotlin=true in gradle.properties, which needs Flutter 3.47 or later, and only once every plugin you depend on has dropped KGP too. Budget an hour for an app with current dependencies, longer if a plugin still applies kotlin-android. It is worth doing now: Flutter already refuses to build with Gradle below 8.14 or AGP below 8.11.1, and has announced it will remove KGP support entirely (flutter#184837).

Everything below was run on Flutter 3.47.4 stable (framework revision 9584c6713b, Dart 3.13), OpenJDK 17.0.20 and Android SDK build-tools 36.1, starting from a project laid out exactly like the Flutter 3.35 android-kotlin template: AGP 8.9.1, KGP 2.1.0, Gradle 8.12, id("kotlin-android") in the app module and a kotlinOptions block. I also checked the final state on Flutter 3.44.8. Each step lists the exact output I got.

Why this migration is not optional anymore

What breaks

AreaChangeSeverity
Gradle wrapperFlutter 3.47 errors below 8.14; AGP 9.1 needs 9.3.1high
kotlin-android in the app moduleFails with built-in Kotlin enabledhigh
kotlinOptions { jvmTarget = ... }Script compilation error under KGP 2.2 and laterhigh
Plugins that apply KGPFail your build once android.builtInKotlin=truehigh
android.newDsl=trueFlutter Gradle Plugin still casts to the old DSL, ClassCastExceptionhigh (keep it false)
KGP entry in settings.gradle.ktsRemoving it drops Kotlin to AGP’s bundled 2.2.10, below Flutter’s floormedium
build.gradle (Groovy) projectsSame edits, different syntax; pre-3.16 buildscript layouts need the declarative plugins migration firstmedium

Pre-flight checklist

Migration steps

  1. Let the Flutter tool add the two opt-out flags, then check them. Run any Android build once with Flutter 3.44 or later. The tool’s migrators append both flags to android/gradle.properties if they are missing. On my project the build still failed (because of Gradle 8.12), but the file was already rewritten:

    # android/gradle.properties, written by the Flutter 3.47.4 migrators
    org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
    android.useAndroidX=true
    # This builtInKotlin flag was added automatically by Flutter migrator
    android.builtInKotlin=false
    # This newDsl flag was added automatically by Flutter migrator
    android.newDsl=false

    The migrator never runs for add-to-app host projects, because the host is a plain Android project. There you add both lines by hand to the host’s gradle.properties. Verify: grep -E 'builtInKotlin|newDsl' android/gradle.properties prints both lines.

  2. Bump the Gradle wrapper to 9.3.1. AGP 9.0.x needs Gradle 9.1.0 or later, and Flutter’s tooling pairs AGP 9.1.x with 9.3.1 or later, which is also what the Flutter 3.47 template ships:

    # android/gradle/wrapper/gradle-wrapper.properties, Flutter 3.47.4
    distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip

    Verify: cd android && ./gradlew --version reports Gradle 9.3.1.

  3. Bump AGP and KGP in settings.gradle.kts, and keep the Kotlin line. These are the versions flutter create writes in 3.47.4:

    // android/settings.gradle.kts, Flutter 3.47.4, AGP 9.1.0, KGP 2.4.0
    plugins {
        id("dev.flutter.flutter-plugin-loader") version "1.0.0"
        id("com.android.application") version "9.1.0" apply false
        id("org.jetbrains.kotlin.android") version "2.4.0" apply false
    }

    It is tempting to delete the org.jetbrains.kotlin.android line, since the point of the migration is to stop using KGP. Do not. With apply false it only puts that Kotlin version on the build classpath, and built-in Kotlin compiles with it. When I removed it, AGP 9.1.0 fell back to its bundled Kotlin 2.2.10, and the Flutter Gradle Plugin rejected the build: Your project's Kotlin version (2.2.10) is lower than Flutter's minimum supported version of 2.2.20. The line also matters while builtInKotlin=false, because then the Flutter Gradle Plugin applies kotlin-android itself to every Android subproject that does not, and it needs KGP on the classpath to do it.

    Verify: nothing yet. The build still fails until step 4.

  4. Replace kotlinOptions with the compilerOptions DSL. This is the edit that surprises people, because it is required even before you touch built-in Kotlin. With AGP 9.1.0, KGP 2.4.0, kotlin-android still applied and builtInKotlin=false, my build failed during script compilation:

    Script compilation errors:
      Line 18:     kotlinOptions {
                   ^ 'fun BaseAppModuleExtension.kotlinOptions(configure: Action<DeprecatedKotlinJvmOptions>): Unit' is deprecated. Please migrate to the compilerOptions DSL.
      Line 19:         jvmTarget = JavaVersion.VERSION_11.toString()
                       ^ 'var jvmTarget: String' is deprecated. Please migrate to the compilerOptions DSL.

    Kotlin 2.2.0 raised the kotlinOptions deprecation to an error, and Flutter 3.47 will not let you stay below KGP 2.2.20, so there is no version combination where kotlinOptions survives. Move the JVM target out of the android {} block into a top-level kotlin {} block:

    // android/app/build.gradle.kts, Flutter 3.47.4, AGP 9.1.0, KGP 2.4.0
    android {
        // ...
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_17
            targetCompatibility = JavaVersion.VERSION_17
        }
        // kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() }  <- delete
    }
    
    kotlin {
        compilerOptions {
            jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
        }
    }

    Keep jvmTarget equal to targetCompatibility. Old templates used 11, new ones use 17; either works as long as the two agree. Verify: flutter build apk --debug succeeds. At this point it also prints WARNING: Your Android app project: app ... applies the Kotlin Gradle Plugin, which will cause build failures in future versions of Flutter. That warning is expected, and it is what step 5 removes.

  5. Remove kotlin-android from the app module. Delete the plugin line and nothing else:

    // android/app/build.gradle.kts, Flutter 3.47.4
    plugins {
        id("com.android.application")
        // id("kotlin-android")  <- delete
        // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
        id("dev.flutter.flutter-gradle-plugin")
    }

    If your app module uses the version catalog form, the line to delete is alias(libs.plugins.kotlin.android). For a Groovy build.gradle, it is apply plugin: 'kotlin-android' or id "kotlin-android", and the kotlin { compilerOptions { ... } } block from step 4 is valid Groovy as written. Verify: flutter build apk --debug succeeds with no KGP warning for app. With builtInKotlin still false, the Flutter Gradle Plugin now applies KGP on your behalf, which is why this intermediate state builds.

  6. Find the plugins that still apply KGP. Build once more with builtInKotlin=false and read the Gradle output. Flutter 3.47 names them for you:

    WARNING: Your app uses the following plugins that apply Kotlin Gradle Plugin (KGP): oldplug
    Future versions of Flutter will fail to build if your app uses plugins that apply KGP.
    Please check the changelogs of these plugins and upgrade to a version that supports Built-in Kotlin.

    For each plugin listed, check pub.dev for a newer version whose changelog mentions built-in Kotlin or AGP 9, and upgrade. If none exists, file an issue with the plugin (Flutter’s app developer guide includes an issue template) and stop here: you are on AGP 9 with built-in Kotlin disabled, which is a supported state. Verify: the warning no longer lists any plugin.

  7. Enable built-in Kotlin. Only once step 6 comes back clean:

    # android/gradle.properties, Flutter 3.47.4, AGP 9.1.0
    android.builtInKotlin=true
    android.newDsl=false

    Leave android.newDsl=false. Verify: flutter build apk --debug succeeds with no KGP warnings, and then flutter run on a device or emulator launches the app.

Verification checklist

Rollback plan

The migration is reversible at every step, and the cheapest rollback is partial. If a plugin breaks after step 7, set android.builtInKotlin=false again: with that flag AGP 9 accepts KGP, and the Flutter Gradle Plugin re-applies kotlin-android to modules that need it, so you do not need to restore the kotlin-android line in your app module. A full rollback to AGP 8 means restoring settings.gradle.kts and the wrapper from git, but on Flutter 3.47 you cannot go below AGP 8.11.1, Gradle 8.14 or KGP 2.2.20, so “rollback” really means AGP 8.11+, not your original 8.9. The kotlin { compilerOptions } change from step 4 stays either way.

Gotchas I hit on the way

The first error is not about Kotlin at all. On the unmigrated project, Flutter 3.47.4 printed the real problem (Gradle 8.12 below 8.14) inside Gradle’s output, then a boxed “Flutter Fix” underneath saying Starting AGP 9+, only the new DSL interface will be read and suggesting you opt out of android.newDsl. The project was on AGP 8.9.1. The box is a heuristic that fires on any failure to apply the Flutter Gradle Plugin, so read the * What went wrong: section first, the same advice as in the assembleDebug exit code 1 guide.

The error message for a leftover kotlin-android depends on your KGP version. With KGP 2.4.0 it is explicit:

> 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: app/build.gradle.kts.

With older KGP versions the same mistake shows up as Cannot add extension with name 'kotlin', which is the form most Stack Overflow answers quote. The Solution: line is useful when the offender is a plugin: for my test plugin it pointed at ../../oldplug/android/build.gradle.kts. For a pub.dev package the path points into ~/.pub-cache/hosted/pub.dev/<package>-<version>/android/, which tells you exactly which package to upgrade. Do not edit files in the pub cache; they are overwritten on the next pub get.

android.newDsl=true is still a hard failure. With everything else migrated, setting it produced class com.android.build.gradle.internal.dsl.ApplicationExtensionImpl$AgpDecorated_Decorated cannot be cast to class com.android.build.gradle.AbstractAppExtension. The Flutter Gradle Plugin still reads the legacy DSL types (flutter#180137 tracks the port). Leave the flag at false until a Flutter release says otherwise, and plan for that release to arrive before AGP 10.

Flutter 3.44 half-works with built-in Kotlin enabled. The docs say android.builtInKotlin=true needs 3.47. I ran the fully migrated project on Flutter 3.44.8 anyway: the APK built and MainActivity was in the dex, but the tool printed Applying the Kotlin Android Plugin (KGP) was unsuccessful. KGP was not found on the classpath. That comes from 3.44’s Flutter Gradle Plugin, which does not read the flag and tries to apply KGP regardless. It is harmless in a trivial app and confusing in a CI log, so treat 3.47 as the real minimum, exactly as documented.

Pre-3.16 projects need an earlier migration first. If your android/build.gradle still has buildscript { ext.kotlin_version = '...' } and your app module uses apply from: ".../flutter.gradle", the steps above do not map cleanly. Do the declarative plugins migration first, then come back to step 2. I did not reproduce that layout for this post; the Flutter doc is the reference there. If you are stuck on the old Kotlin version message instead, the KGP version error post covers where that version lives in older layouts.

Gradle 9 surfaces other old warnings as errors. Gradle 9 removed APIs that Gradle 8 only deprecated, so an old plugin can fail for reasons unrelated to Kotlin. If a JDK 24 warning like A restricted method in java.lang.System has been called shows up alongside, that one is covered in its own post.

Measured results

Project state (Flutter 3.47.4 unless noted)Result
AGP 8.9.1, KGP 2.1.0, Gradle 8.12, kotlin-androidFails: Gradle below 8.14
AGP 9.1.0, KGP 2.4.0, Gradle 9.3.1, kotlinOptions keptFails: script compilation errors
Same, compilerOptions, kotlin-android kept, builtInKotlin=falseBuilds, KGP warning for app
Same, builtInKotlin=trueFails: KGP no longer required since AGP 9.0
kotlin-android removed, builtInKotlin=trueBuilds, 4.0 s incremental
KGP line removed from settings.gradle.ktsFails: Kotlin 2.2.10 below 2.2.20
Plugin applying KGP, builtInKotlin=trueFails, names the plugin’s build file
Plugin applying KGP, builtInKotlin=falseBuilds, warning lists the plugin
Migrated, newDsl=trueFails: ClassCastException in the Flutter Gradle Plugin
Migrated, builtInKotlin=true, Flutter 3.44.8Builds, misleading “KGP was not found” message

Sources

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Back