skip to content

After moving a Flutter Android app to Android Gradle Plugin 9, what do android.builtInKotlin and android.newDsl in gradle.properties do, and what migration does the app need?

level: seniorimportance: nice to knowfreq 22%

answer

  1. AGP 9 makes built-in Kotlin default
  2. kotlin-android plugin must go
  3. kotlinOptions becomes kotlin.compilerOptions
  4. flags are temporary escape hatches
  5. all plugins must migrate first

basics

~10 s

AGP 9 compiles Kotlin itself and knows only its new DSL; Flutter's android.builtInKotlin=false and android.newDsl=false are temporary escape hatches. Migrating removes kotlin-android and kotlinOptions and adds kotlin { compilerOptions { ... } }.

solid answer

~40 s

Android Gradle Plugin 9 makes **built-in Kotlin** the default and uses only its new DSL interfaces, so apps still applying the `kotlin-android` plugin fail to build. Flutter 3.44 added support for keeping the old Kotlin Gradle Plugin with `android.builtInKotlin=false`, and the Flutter tool's migrator writes that flag and `android.newDsl=false` into `gradle.properties` when they are missing. To migrate an app, remove `id("kotlin-android")` and the `kotlinOptions` block from `android/app/build.gradle.kts` and add `kotlin { compilerOptions { jvmTarget = JvmTarget.JVM_17 } }`. Flutter 3.47 supports `android.builtInKotlin=true` once the app and all its plugins have migrated. Separately, product flavors using `resValue` fail under AGP 9 by default; move those values into flavor `strings.xml` files.

code

kotlin · 17 lines
kotlin
plugins {
    id("com.android.application")
    // id("kotlin-android") removed: AGP 9 compiles Kotlin itself
    id("dev.flutter.flutter-gradle-plugin")
}

android {
    namespace = "com.example.plantcare"
    compileSdk = flutter.compileSdkVersion
    // kotlinOptions { jvmTarget = ... } removed
}

kotlin {
    compilerOptions {
        jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
    }
}

go deeper

for a junior

Recall that AGP 9 compiles Kotlin itself, so the kotlin-android plugin has to come out of the app's Gradle file eventually.

for a middle

Explain the two gradle.properties flags, what the Flutter migrator writes, and the three edits to build.gradle.kts.

for a senior

Sequence the migration across app and plugins, handle add-to-app hosts by hand, and fix flavor resValue failures before they block releases.

for a principal

Plan platform migrations across teams: track plugin readiness, budget for the announced removal of KGP support, and avoid permanent escape hatches.

## What changed in AGP 9 The **Android Gradle Plugin (AGP)** is the Gradle plugin that builds every Android app, including a Flutter app's Android shell. AGP 9 brought two changes that affect Flutter projects: 1. **Built-in Kotlin is the default.** AGP now compiles Kotlin itself. Projects that also apply the separate **Kotlin Gradle Plugin** (KGP, applied as `kotlin-android` or `org.jetbrains.kotlin.android`) fail to build without migration. 2. **Only the new AGP DSL interfaces.** Build scripts and plugins using old DSL types are not recognised. Many Flutter apps created before these releases apply `kotlin-android` in `android/app/build.gradle.kts`, and many plugins do too. ## The escape hatches To keep projects building during the transition, Flutter provides two `gradle.properties` flags: - `android.builtInKotlin=false` — keep using KGP under AGP 9 (support added in Flutter 3.44); - `android.newDsl=false` — keep the legacy DSL types working. The Flutter tool includes **migrators** that append these lines to `gradle.properties` when they are missing, the next time you run `flutter run` or `flutter build apk`; new project templates contain them too. Add-to-app host projects are pure Android projects where the migrator does not run, so they must add both lines by hand. These flags are **temporary**: the Flutter docs state that support for applying KGP and for the old DSL will be removed in a future version. ## Migrating the app In `android/app/build.gradle.kts`: 1. **Remove** `id("kotlin-android")` from the `plugins {}` block. 2. **Remove** the `kotlinOptions { jvmTarget = ... }` block inside `android {}`. 3. **Add** a top-level `kotlin` block: ```kotlin kotlin { compilerOptions { jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 } } ``` The Flutter guide warns that this migration applies only to apps that **already apply KGP**; an app that never applied `kotlin-android` needs only `android.newDsl=false` and no further steps. ## When to flip `builtInKotlin` to `true` Flutter 3.47 added support for **enabling** built-in Kotlin with `android.builtInKotlin=true` once the **app and all of its plugins** have migrated. A single plugin still applying KGP keeps the flag at `false`, so the practical order is: - migrate the app's own Gradle file; - upgrade plugins to versions that have migrated (plugin authors have their own guide); - only then set `android.builtInKotlin=true` and build every flavor. ## A related AGP 9 break: `resValue` in flavors Flavored apps often set the launcher name with `resValue("string", "app_name", "...")` in each product flavor. The Flutter flavor guide notes that builds with AGP 9 or later fail by default with an error saying the product flavor contains custom resource values but the feature is disabled. The fix is to delete those `resValue` calls and define `app_name` in `android/app/src/<flavor>/res/values/strings.xml`. ## What a new project looks like A project created with Flutter 3.47's template already reflects the new layout: `settings.gradle.kts` pins AGP 9.1.0, the app's `build.gradle.kts` applies only `com.android.application` and the Flutter Gradle plugin and uses `kotlin { compilerOptions { ... } }`, and `gradle.properties` carries the two flags set to `false`. | Item | Before AGP 9 | After migration | |---|---|---| | Kotlin compilation | `kotlin-android` plugin | AGP built-in Kotlin | | JVM target | `kotlinOptions { jvmTarget }` | `kotlin { compilerOptions { jvmTarget } }` | | Transitional flags | none | `android.builtInKotlin`, `android.newDsl` | | Flavor app names | `resValue` in the flavor | flavor `strings.xml` | ## Checking readiness A practical sequence for a real app: 1. Run `flutter build apk` once after upgrading Flutter so the migrators add the two flags if missing. 2. Search `android/` for `kotlin-android` and `kotlinOptions` and migrate the app module. 3. For each plugin in `pubspec.lock`, check whether its latest version has moved off KGP; upgrade the ones that have. 4. Try `android.builtInKotlin=true` on a branch and build every flavor and build mode; any plugin still applying KGP fails loudly there. 5. Keep `android.newDsl=false` until Flutter's own Gradle plugin and your plugins no longer need the old DSL. ## Interview angle The question tests whether a candidate can read a platform migration rather than paste flags: the flags buy time, the plugin ecosystem decides when `builtInKotlin=true` is safe, and the removal of KGP support is already announced.

  • Why can't an app set android.builtInKotlin=true as soon as its own Gradle file is migrated?
    Plugins are Gradle subprojects built with the same setting. If any plugin still applies the Kotlin Gradle Plugin, built-in Kotlin breaks that plugin's build. Flutter 3.47 supports `true` only once the app and all its plugins have migrated.
  • An add-to-app Android host fails under AGP 9 while the Flutter module builds. What is missing?
    The host is a pure Android project, so the Flutter migrator never runs there. Add `android.builtInKotlin=false` and `android.newDsl=false` to the host's `gradle.properties` by hand, then migrate its Kotlin setup when ready.

saying these in an interview costs you the question

  • Treats android.builtInKotlin=false as the permanent fix
  • Sets builtInKotlin=true while plugins still apply kotlin-android
  • Migrates an app that never applied the Kotlin Gradle Plugin
  • Keeps resValue app names in flavors under AGP 9
  • Expects the Flutter migrator to update add-to-app host projects