Flutter 项目 Gradle 迁移实战指南:从 Gradle 3.3 / AGP 2.3.3 升级到 Gradle 4.1 / AGP 3.0.1
本指南基于 Flutter 仓库中一份历史性但极具参考价值的迁移文档《Upgrading Flutter projects to Gradle 4.1 and Android Studio Gradle plugin 3.0.1》展开,结合当前仓库中 examples/hello_world 与 packages/flutter_tools/templates 中的真实工程与模板源码进行对照解读。它面向的是 2017 年 12 月之前创建、Android 侧仍停留在旧版构建体系的 Flutter 工程,读者将掌握:为何需要手动升级、App 与 Plugin 两类工程各自的精确改动清单、关键报错背后的原因,以及这些历史约定在今天 Flutter 项目模板中的演化形态。
一、背景:为什么要升级
这份文档所描述的迁移,源于 Flutter 工具链在 2017 年 12 月的一次重大变更。当时,PR #13492(2017-12-13 合并)使 Flutter 工具链创建的新项目全面转向新的 Android 构建体系。新旧对比非常明确:
| 维度 | 旧模式(2017-12 之前创建的项目) | 新模式(#13492 合并后创建的项目) |
|---|---|---|
| Gradle 版本 | 3.3 | 4.1 |
| Android Studio Gradle 插件(AGP) | 2.3.3 | 3.0.1 |
文档开篇即点明适用范围:"本页面适用于 2017 年 12 月之前编写的代码迁移,如今已不太可能仍然适用",因此它首先是一份历史档案式的手工迁移指南。即便如此,它依然有现实价值:如果你仍维护着那个时期遗留的老工程,或在做构建体系的考古式排障,这份清单仍是少数能精确到"第几行、改几处"的操作文档。
什么情况下必须手动升级? 关键触发点是 Flutter 插件的兼容性。当 Flutter 生态中的插件开始按"新模式"(Gradle 4.1 + AGP 3.0.1)打包发布时,旧模式的宿主工程在消费这些插件时会遇到构建问题,此时就需要按本文步骤升级。
注意:当时 Android 官方同步提供了 Android Plugin 3.0.0 迁移指南,Flutter 的这份迁移步骤很大程度上是在其基础上结合 Flutter 工程的特殊结构(
android/与android/app/两层 Gradle 工程、插件通过:app子工程评估顺序等)定制的。
二、升级 Flutter App 工程
一个 Flutter App 工程的 Android 宿主包含两个 Gradle 工程文件:外层 android/build.gradle(工程级)与内层 android/app/build.gradle(模块级),另有 android/gradle/wrapper/gradle-wrapper.properties(Gradle 版本)。升级共四步。
第 1 步:升级 Gradle Wrapper
打开 android/gradle/wrapper/gradle-wrapper.properties,把最后一行 distributionUrl 中的 Gradle 版本号从 3.3 改为 4.1。
# 修改前
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
# 修改后
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
仓库中当前各示例工程保留了 wrapper 的结构,只是版本已更新到现代值。以 examples/hello_world/android/gradle/wrapper/gradle-wrapper.properties 为例,其内容仍维持五行的固定骨架,只是 distributionUrl 已指向 gradle-9.3.1-all.zip——这说明"改版本号"这一步的形态从 2017 年至今没有变化,变化的只是具体版本。新工程模板则通过 packages/flutter_tools/templates/app/android.tmpl/gradle/wrapper/gradle-wrapper.properties.tmpl 中的占位符 gradle-{{gradleVersion}}-all.zip 注入版本,由 flutter create 时动态生成。
第 2 步:改写 android/build.gradle 的仓库与 AGP 版本
首先,仓库声明需要做两处等价的替换(文档强调"twice",即以下新旧代码块替换会在 buildscript 与 allprojects 两个块中各出现一次,需分别处理):
# 修改前(旧写法,每处都要替换)
repositories {
jcenter()
maven {
url "https://maven.google.com"
}
}
# 修改后
repositories {
google()
jcenter()
}
这一改动的本质,是 AGP 3.0 起 Google 官方推出了 google() 仓库快捷语法,用来替代手写的 maven { url "https://maven.google.com" }——两者指向同一个 Google Maven 仓库,后者是前者在旧 Gradle 上的等价物。它虽然只是语法糖,却直接关系到 AGP 3.0.1 及其依赖的 com.android.support、com.google.android 等坐标能否被正确解析。
其次,buildscript 的 classpath 依赖要升级 AGP:
# 修改前
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
}
# 修改后
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
最后,拆分 subprojects 块。旧写法把构建目录重定向与模块评估绑定在同一个闭包里:
# 修改前
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
project.evaluationDependsOn(':app')
}
需要拆成两个独立的 subprojects 块:
# 修改后
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
这一条是文档中最为"反直觉"的改动,其目的在原文档中有明确说明:避免当你的 App 依赖了"名字按字典序排在 app 之前"的插件时,抛出 "Gradle build failed to produce an Android package" 错误。根本原因在于:所有插件子工程与 :app 都共享同一个 evaluation 时机,若不把 evaluationDependsOn(':app') 与 buildDir 重定向解耦,那些按字母序先于 app 被评估的插件子工程会在 :app 尚未配置完时就尝试产出 Android 包,从而触发该报错。
有趣的是,这一"解耦思想"在现代模板中仍清晰可见。当前 packages/flutter_tools/templates/app/android-java.tmpl/build.gradle.kts.tmpl 依然用两个独立的 subprojects {} 块分别处理 buildDirectory 重定向(统一收敛到根 build 目录下)与 evaluationDependsOn(":app"),只是 Groovy DSL 换成了 Kotlin DSL:
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
第 3 步:升级 android/app/build.gradle 的 SDK 版本
在 android/app/build.gradle 中,需要把 SDK 与 build-tools 版本从 25 系升到 27 系,全文共 3 处:compileSdkVersion、targetSdkVersion 与 buildToolsVersion。即 25 全部替换为 27、25.0.3 全部替换为 27.0.3。
这一步与 AGP 3.0.1 强相关:AGP 3.0 是首个强制要求 compileSdkVersion 不低于 26 的插件版本,同时配套了新的 Android 8.0(API 27)构建工具 27.0.3。如果沿用旧的 SDK 25 / build-tools 25.0.3,AGP 3.0.1 本身就会给出版本过低的校验错误。
历史说明:在 Flutter 后来引入
profile构建类型的版本之前,还曾要求往android.buildTypes补充profile { matchingFallbacks = ['debug', 'release'] }片段。若你使用的 Flutter 版本早于 PR #13558,必须手动加上这段,否则flutter run --profile等以 profile 模式触发的构建会因找不到可匹配的构建类型而失败。
第 4 步:重命名依赖配置并升级测试依赖
AGP 3.0 引入了新的依赖配置体系,旧配置名被废弃,需要成对映射:
| 旧配置名 | 新配置名 | 说明 |
|---|---|---|
compile |
api 或 implementation |
api 透传依赖到编译期,implementation 则仅在模块内部可见(推荐用于减少编译暴露) |
provided |
compileOnly |
仅在编译期可见、不打包进产物 |
androidTestCompile |
androidTestImplementation |
Android 仪器测试依赖 |
testCompile |
testImplementation |
单元测试依赖 |
文档强调,dependencies 中凡是你自定义的依赖声明都要做这种改名。同时,Flutter 默认模板也顺势把旧的测试依赖升级到了与新配置配套的新版本:
# 修改前(Flutter 默认模板的旧写法)
dependencies {
androidTestCompile 'com.android.support:support-annotations:25.4.0'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.android.support.test:rules:0.5'
}
# 修改后(跟随 Android Studio 官方模板的做法)
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
这里的升级包含三层变化:配置名从 androidTestCompile 换成 androidTestImplementation;runner 从 0.5 升到 1.0.1;新增了 junit:junit:4.12(本地单元测试)与 espresso-core:3.0.1(UI 测试),同时删掉了纯注解库 support-annotations——整体上"跟随 Android Studio 官方模板的引导"完成了一次依赖瘦身与现代化。
三、升级 Flutter 插件工程
Flutter 插件工程(Plugin)与 App 工程的差异在于:插件本身通常有一个顶层 android/ 目录作为发布给使用者的 Gradle 模块,同时还内嵌一个 example/ App 用于开发调试。因此升级动作分为四步。
第 1 步:按上文完整升级 example/ App 工程。 插件的 example 就是标准 Flutter App,前文四个步骤全部适用。
第 2 步:删除插件顶层 android/ 目录中的 Gradle Wrapper 文件。 即删除 android/gradle/ 目录以及 android/gradlew、android/gradlew.bat(如果存在)。理由是职责单一化:Gradle Wrapper 仅由 example App 使用,而它自己已经携带了 wrapper;插件模块发布后是被使用者的宿主工程构建的,理论上应继承使用者的 Gradle 版本,不应该再自带一份 wrapper 干扰依赖解析。
第 3 步:改写插件自身的 android/build.gradle。 与 App 工程第 2 步完全相同,做三处替换:
buildscript与(如果有)allprojects中的仓库块,从jcenter() + maven { url "https://maven.google.com" }改为google() + jcenter()(两处);classpath 'com.android.tools.build:gradle:2.3.3'改为classpath 'com.android.tools.build:gradle:3.0.1';- 将版本号
25替换为27、25.0.3替换为27.0.3(对应 compileSdkVersion / buildToolsVersion 等字段)。
第 4 步:重命名任何自定义 dependencies 中的配置。 规则与 App 工程第 4 步一致:compile → api(或 implementation)、provided → compileOnly。
升级完成后,插件模块对外只暴露构建逻辑,其自身对构建工具链的要求由 AGP/Gradle 版本声明体现,具体依赖则由使用者的宿主工程决定,这也是插件工程与 App 工程升级路径最本质的分野。
四、升级后的验证与常见问题
完成上述修改后,建议按以下顺序验证:
- 在
android/目录执行 Gradle 同步(Android Studio 的 "Sync Project with Gradle Files"),确认依赖能被google()/jcenter()正常解析,无 404 或版本冲突。 - 运行
flutter build apk(或flutter run)做一次完整构建,确认不再出现 "Gradle build failed to produce an Android package"。 - 若有自定义插件依赖,逐一检查其
dependencies块是否还有残留的compile/provided/androidTestCompile旧配置名——AGP 3.0 对废弃配置名默认会报错而非告警。
原文档针对性的报错排查要点可归纳为下表:
| 现象 | 根因与对策 |
|---|---|
| "Gradle build failed to produce an Android package" | 插件子工程名按字典序排在 app 之前且与 :app 共享 evaluation 时机,需拆分 subprojects 块(见 2.2 节第三步) |
| AGP 版本过低类报错 | buildscript classpath 仍指向 2.3.3,需升级到 3.0.1 |
| compileSdk / buildTools 校验失败 | SDK 25 / build-tools 25.0.3 需升到 27 / 27.0.3 |
| 无法解析 Google Maven 上的依赖 | 仓库块仍用手写 maven { url ... },应改用 google() |
| profile 构建类型缺失 | 若 Flutter 版本早于 #13558,需补 profile { matchingFallbacks = ['debug', 'release'] } |
五、以史为鉴:这套约定在现代 Flutter 模板中的演化
读完历史迁移步骤,再看当前仓库中 Flutter 工具链实际生成的新工程,会发现当年确立的"形态约定"大多保留至今,只是载体现代化了。
1. Gradle 与 AGP 的版本来源发生了转移。 如今 wrapper 版本不再由用户在属性文件中手写,而是由模板占位符注入:见 packages/flutter_tools/templates/app/android.tmpl/gradle/wrapper/gradle-wrapper.properties.tmpl;AGP 版本则被 {{agpVersion}} 占位符统一管理。实际落地工程以 examples/hello_world 为例,其 gradle-wrapper.properties 已指向 Gradle 9.3.1,构建体系演进到了 Kotlin DSL + 多版本插件管理。
2. 仓库声明收敛为 google() + mavenCentral()。 当年迁移目标是 google() + jcenter(),而 jcenter() 后来已停服,因此现代模板(如 packages/flutter_tools/templates/app/android-java.tmpl/build.gradle.kts.tmpl)统一写成 google() 与 mavenCentral();插件解析入口则迁移到了 settings.gradle.kts.tmpl 的 pluginManagement 块(含 gradlePluginPortal())。
3. AGP 不再通过 classpath 手动声明,而是走插件管理。 新工程的 settings.gradle.kts.tmpl 中用 id("com.android.application") version "{{agpVersion}}" apply false 一次声明 AGP 版本,app 模块再通过 plugins { id("com.android.application") ... } 应用。当年"在 buildscript classpath 里钉死 2.3.3 / 3.0.1"的写法就此退出历史舞台。
4. SDK 版本不再硬编码,改为由 Flutter Gradle 插件提供。 现代 app/build.gradle.kts.tmpl 中统一写作 compileSdk = flutter.compileSdkVersion、minSdk = flutter.minSdkVersion、targetSdk = flutter.targetSdkVersion——这与 2017 年"手动把 25 改成 27"形成了鲜明对照:版本演进逻辑被收敛到 Flutter SDK 侧统一管理,Flutter 工具在 packages/flutter_tools/lib/src/android/gradle_utils.dart 等处集中定义并做一致性校验。
5. subprojects 解耦与 :app 评估顺序约定被完整继承。 这是迁移文档中最具"工程智慧"的一条,现代 Kotlin DSL 模板原样保留了"两个独立 subprojects 块分别处理构建目录与 evaluationDependsOn(":app")"的结构,可见它在 Flutter 的 Android 构建模型中是长期稳定、不可省略的一环。
6. 构建目录约定延续。 当年 project.buildDir = "${rootProject.buildDir}/${project.name}" 把各子工程构建产物统一收敛到根 build 目录;现代模板(build.gradle.kts.tmpl)只是改用 rootProject.layout.buildDirectory.dir("../../build") 的 Kotlin DSL API 表达了完全相同的意图。
六、结语
这份 2017 年的迁移文档之所以至今值得研读,是因为它浓缩了 Flutter Android 构建体系中一批"元约定"的来历:google() 仓库形态、compile → api/implementation 配置更名、subprojects 与 :app 的评估解耦、Flutter 插件与宿主工程在 Gradle 职责上的划分。对于仍持有旧工程、需要手动迁移的开发者,按本文步骤逐文件核对即可安全落地;对于新工程开发者,则可借本文看懂 flutter create 生成的那些 Gradle 文件为什么长成今天的样子。若在迁移中遇到模棱两可的细节,建议对照 examples/hello_world 这类仓库内的现代示例工程,或参考当时的 Android 官方 AGP 3.0.0 迁移文档进行双源校验。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00