Flutter 3.44 插件内置 Kotlin (KGP) 双向兼容适配指南
# 一、简述
最近升级到 Flutter 3.44.x 及以上版本的朋友,想必在编译项目时都被控制台这行显眼的警告刷过屏:
WARNING: Your app uses the following plugins that apply Kotlin Gradle Plugin (KGP)...
Future versions of Flutter will fail to build if your app uses plugins that apply KGP.
万恶的 KGP(Kotlin Gradle Plugin)要被“抛弃”了!
原来,从 Android Gradle Plugin (AGP) 9.0 开始,对 Kotlin 的支持被彻底收归“内置”(Built-in Kotlin)。任何在插件中单方面显式声明的 apply plugin: 'kotlin-android',在未来的 Flutter 编译环境中都会直接导致构建崩溃。
官方指引(Built-in Kotlin migration for plugin authors (opens new window))中给的路线非常直接:只要把插件的最低 Flutter 版本限制一刀切升到 3.44,然后删掉 apply plugin: 'kotlin-android',收工!
但是,在实际开源组件的维护中,如果我们作为维护者强行把最低版本拔高到 Flutter 3.44,这就相当于与那些还在坚守 Flutter 3.x 早期版本(甚至 Flutter 3.0)的用户们“割袍断义”了!所以,本篇指南就带各位朋友搞一套新旧 Flutter 双向兼容的适配秘籍。
# 二、新旧版本对比
在开整之前,我们先用一张表直观对比一下新旧两种编译环境下的机制差异。知己知彼,才好“对症下药”:
| 维度 | 旧版 Flutter 构建体系 (< 3.44) | 新版 Flutter 构建体系 (>= 3.44 / AGP 9+) |
|---|---|---|
| Kotlin 加载机制 | 依赖插件通过 kotlin-android 显式激活 | 由 AGP 9.0+ 内置处理,插件手动 apply 会与内置逻辑产生冲突 |
| Kotlin 插件配置 | 必须在 buildscript.dependencies 中引入 KGP 依赖 | 移交至主 App 项目或由 Flutter 构建链管理,子插件中无需重复声明 |
| JVM Target 设置 | 必须在 android 内部通过 kotlinOptions {} 指定 | 引入了外层的 kotlin { compilerOptions {} } 新型 DSL |
# 三、动态兼容方案
这里以我们维护的 chat_bottom_container (opens new window) 插件为例,为了让插件在旧版 Flutter (AGP < 9.0) 环境下能正常编译,而在新版 Built-in Kotlin (AGP >= 9.0) 下又不会因 KGP 冲突报错,我们需要在 chat_bottom_container/android/build.gradle (opens new window) 中采取“动态嗅探”策略。
# 1、动态应用 kotlin-android 插件
我们通过解析 AGP 实际的版本号以及检查宿主工程是否开启了内置 Kotlin,来决定是否手动 apply 插件:
// 1. 获取当前编译环境中 Android Gradle 插件的主版本号
def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize(".")[0] as int
// 2. 判断当前内置 Kotlin 是否生效(要求 AGP 版本 >= 9 且未被显式禁用)
def builtInKotlinActive = agpMajor >= 9 &&
(!project.hasProperty("android.builtInKotlin") ||
Boolean.parseBoolean(project.property("android.builtInKotlin").toString()))
// 3. 仅在内置 Kotlin 不生效的旧版构建环境下,才手动应用旧版 kotlin-android 插件
if (!builtInKotlinActive) {
apply plugin: "kotlin-" + "android"
}
- 在 Gradle 配置阶段,从
com.android.Version中获取当前的 AGP 主版本号(例如:8 或 9)。 - 结合
agpMajor和android.builtInKotlin属性,计算出builtInKotlinActive布尔值,用于判定当前内置 Kotlin 是否处于活动状态。 - 如果宿主工程依旧没有启用 Built-in Kotlin,我们就打上“补丁”,通过动态拼接字符串的方式手动执行
apply plugin,在保持向后兼容的同时避免被 Flutter 误判。
Q:为什么不直接用
apply plugin: "kotlin-android"?A:这是为了避免由于 Flutter CLI 静态正则扫描机制的局限性导致的误报。
在构建应用时,Flutter 构建工具(
flutter_tools)并不会去真正解析并执行 Groovy 的if-else条件分支,它只是简单粗暴地使用静态正则表达式(RegExp)扫描依赖插件的build.gradle文本。只要文件里出现了apply plugin: "kotlin-android"或id "org.jetbrains.kotlin.android"的字面量,不管这段代码处于什么禁用分支内,都会被判定为“手动应用了 KGP”,从而抛出警告。
# 2、动态配置 JVM 目标版本 (jvmTarget)
由于 Kotlin 插件版本的不同,设置 JVM 目标的 DSL 语法也有两套。如果直接写新语法,老项目在老版本 KGP 下编译会报错(找不到属性);写旧语法,在新环境编译时又会抛出过时或不支持的错误。
因此,我们需要采用如下动态属性嗅探法:
// 1. 获取 project 中的 kotlin 扩展配置对象
def kotlinExt = project.extensions.findByName("kotlin")
// 2. 判断该扩展对象中是否支持 compilerOptions 属性
if (kotlinExt?.hasProperty("compilerOptions")) {
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
}
}
} else {
// 3. 降级使用传统的 kotlinOptions 配置方式,防止老版 Kotlin Gradle 插件报错
android.kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
- 通过
extensions.findByName("kotlin")安全获取 Kotlin 扩展对象,防止因项目未引入 Kotlin 导致直接报空指针异常。 - 如果该扩展包含了新的
compilerOptions,说明当前处于新版 KGP (1.9+) 或内置 Kotlin 环境,此时直接使用新语法compilerOptions配置 JVM 目标。 - 若为老版本,则降级使用传统的
android.kotlinOptions { jvmTarget = '1.8' }语法,以保障老项目顺利编译通过。
# 四、最后
通过以上这两处 Gradle 动态判定以及巧妙的字面量拼接,插件就可以在不做任何 Breaking Change 的前提下,平滑兼容新旧版本的 Flutter 和 Kotlin 编译环境。
对于使用者来说,直接引入修改后的插件即可,无需在主项目的 gradle.properties 中强行添加 android.builtInKotlin=false 这类治标不治本的规避配置。
希望本篇秘籍能帮各位朋友少踩几个编译坑,下期再会!

- 01
- Obsidian - 使用 Share Note 分享笔记并自部署06-21
- 02
- AI - Antigravity 不认全局 skills 怎么办?03-02
- 03
- AI - 通过 Docker 来安装与访问 OpenClaw02-02