首页
/ Flutter 项目 Gradle 迁移实战指南:从 Gradle 3.3 / AGP 2.3.3 升级到 Gradle 4.1 / AGP 3.0.1

Flutter 项目 Gradle 迁移实战指南:从 Gradle 3.3 / AGP 2.3.3 升级到 Gradle 4.1 / AGP 3.0.1

2026-09-06 19:14:39作者:裘晴惠Vivianne

本指南基于 Flutter 仓库中一份历史性但极具参考价值的迁移文档《Upgrading Flutter projects to Gradle 4.1 and Android Studio Gradle plugin 3.0.1》展开,结合当前仓库中 examples/hello_worldpackages/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",即以下新旧代码块替换会在 buildscriptallprojects 两个块中各出现一次,需分别处理):

# 修改前(旧写法,每处都要替换)
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.supportcom.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 处compileSdkVersiontargetSdkVersionbuildToolsVersion。即 25 全部替换为 2725.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 apiimplementation 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/gradlewandroid/gradlew.bat(如果存在)。理由是职责单一化:Gradle Wrapper 仅由 example App 使用,而它自己已经携带了 wrapper;插件模块发布后是被使用者的宿主工程构建的,理论上应继承使用者的 Gradle 版本,不应该再自带一份 wrapper 干扰依赖解析。

第 3 步:改写插件自身的 android/build.gradle 与 App 工程第 2 步完全相同,做三处替换:

  1. buildscript 与(如果有)allprojects 中的仓库块,从 jcenter() + maven { url "https://maven.google.com" } 改为 google() + jcenter()(两处);
  2. classpath 'com.android.tools.build:gradle:2.3.3' 改为 classpath 'com.android.tools.build:gradle:3.0.1'
  3. 将版本号 25 替换为 2725.0.3 替换为 27.0.3(对应 compileSdkVersion / buildToolsVersion 等字段)。

第 4 步:重命名任何自定义 dependencies 中的配置。 规则与 App 工程第 4 步一致:compileapi(或 implementation)、providedcompileOnly

升级完成后,插件模块对外只暴露构建逻辑,其自身对构建工具链的要求由 AGP/Gradle 版本声明体现,具体依赖则由使用者的宿主工程决定,这也是插件工程与 App 工程升级路径最本质的分野。

四、升级后的验证与常见问题

完成上述修改后,建议按以下顺序验证:

  1. android/ 目录执行 Gradle 同步(Android Studio 的 "Sync Project with Gradle Files"),确认依赖能被 google() / jcenter() 正常解析,无 404 或版本冲突。
  2. 运行 flutter build apk(或 flutter run)做一次完整构建,确认不再出现 "Gradle build failed to produce an Android package"。
  3. 若有自定义插件依赖,逐一检查其 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.tmplpluginManagement 块(含 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.compileSdkVersionminSdk = flutter.minSdkVersiontargetSdk = 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 迁移文档进行双源校验。

登录后查看全文
热门项目推荐
相关项目推荐