1 - Flutter Android 构建的版本地狱:为什么改一个版本要改一堆?

前言

如果你在构建 Flutter Android 应用时遇到过这样的错误:

Could not resolve all files for configuration ':surface:androidJdkImage'.
Error while executing process jlink with arguments...

然后按照提示升级了 AGP(Android Gradle Plugin),结果又报错说找不到仓库,再加上仓库后又说 Gradle 版本不对,再升级 Gradle 后又说 Kotlin 版本太旧…

恭喜你,欢迎来到 Flutter Android 构建的版本地狱 🔥

本文将深入解析这背后的原因,以及如何优雅地解决这类问题。

一、问题的本质:多层依赖链

Flutter Android 项目的构建涉及一条复杂的依赖链:

Flutter (Google Flutter 团队)
  ↓
Android Gradle Plugin / AGP (Google Android 团队)
  ↓
Gradle (Gradle Inc.)
  ↓
Kotlin (JetBrains)
  ↓
Java/JDK (Oracle/OpenJDK)

关键问题:这些工具由不同的组织开发,各自独立发版,但又必须相互兼容。

典型的版本兼容矩阵

AGP 版本最低 Gradle 版本推荐 Kotlin 版本最低 Java 版本
7.4.07.51.8.0+11
8.0.08.01.8.10+17
8.1.08.01.8.22+17
8.2.18.21.9.0+17
8.3.08.41.9.20+17

一旦某一层不兼容,整个构建就会失败。

二、真实案例分析

案例:Java 21 + AGP 8.1.0 的灾难

场景

  • 你的电脑升级到了 Java 21(可能是 Android Studio 自动更新的)
  • 项目使用 AGP 8.1.0
  • 尝试构建 Release 版本

错误信息

Error while executing process jlink with arguments
{--module-path ... --add-modules java.base ...}

根本原因

AGP 8.1.0 及以下版本在处理 Java 模块系统时有一个已知 bug(Issue 294137077),当遇到 Java 21 的新特性时会失败。

解决方案

1
2
// 必须升级到 AGP 8.2.1+
id "com.android.application" version "8.2.1" apply false

但这只是开始…

连锁反应

升级 AGP 后,你会遇到:

问题 1:找不到依赖

Cannot resolve external dependency com.android.tools.build:gradle:8.2.1
because no repositories are defined.

原因:忘记配置仓库

解决

1
2
3
4
5
6
pluginManagement {
    repositories {
        google()
        mavenCentral()
    }
}

问题 2:Gradle 版本太旧

AGP 8.2.1 requires Gradle 8.2 or higher.
Current version is 7.6.

解决:升级 gradle-wrapper.properties

1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip

问题 3:Kotlin 版本不兼容

This version of Gradle requires Kotlin Gradle plugin version 1.9.0 or higher.

解决

1
id "org.jetbrains.kotlin.android" version "1.9.20" apply false

一个简单的构建错误,最终需要修改 4-5 个配置文件!

三、为什么会这么复杂?

1. 历史包袱

2008 年 - Android 发布
2012 年 - Gradle 成为 Android 官方构建工具
2017 年 - Kotlin 成为 Android 官方语言
2018 年 - Flutter 1.0 发布
2023 年 - Java 21 发布(LTS 版本)

每个工具都有 10+ 年的历史,必须保持向后兼容。

2. 独立团队的协调成本

  • Google Flutter 团队:关注跨平台体验
  • Google Android 团队:关注 Android 生态
  • Gradle Inc.:构建工具公司,服务多个平台
  • JetBrains:Kotlin 语言开发商
  • Oracle/OpenJDK:Java 语言维护者

这些团队发版节奏不同,优先级也不同。

3. 构建工具的特殊性

Gradle 是个自举系统(bootstrap system):

问:用什么下载 Gradle?
答:用 Gradle Wrapper

问:Gradle Wrapper 从哪来?
答:从 Gradle 仓库下载

问:怎么配置仓库?
答:写在 Gradle 配置文件里

问:Gradle 还没下载,怎么读配置文件?
答:...这就是鸡生蛋蛋生鸡的问题

这导致版本协调必须在多个层次手动配置。

四、新版 vs 旧版项目结构

Flutter 在 3.16 版本引入了新的项目结构来简化配置。

旧版结构(Flutter 3.15 及以下)

android/build.gradle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:7.4.0'
        classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0'
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

android/settings.gradle

1
include ':app'

新版结构(Flutter 3.16+)

android/settings.gradle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
pluginManagement {
    repositories {
        google()
        mavenCentral()
    }
}

plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.2.1" apply false
    id "org.jetbrains.kotlin.android" version "1.9.20" apply false
}

include ":app"

android/build.gradle

1
2
3
4
5
6
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

新版结构的优势

  1. 集中管理插件版本:所有插件版本在 settings.gradle
  2. 更清晰的职责分离
    • settings.gradle:插件和仓库配置
    • build.gradle:项目级配置
    • app/build.gradle:应用级配置
  3. 符合 Gradle 最佳实践:使用 pluginManagement

五、如何判断项目结构版本

快速判断法

检查 android/settings.gradle

新版结构

1
2
3
4
5
plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "X.X.X" apply false
    // ↑ 有 plugins 块和 flutter-plugin-loader = 新版
}

旧版结构

1
2
include ':app'
// ↑ 只有简单的 include 语句 = 旧版

判断矩阵

特征新版旧版
settings.gradleplugins
build.gradlebuildscript
包含 flutter-plugin-loader
Flutter 版本3.16+3.15-

六、终极解决方案

方案 A:推荐的稳定版本组合(2024-2025)

1
2
3
4
5
6
# 推荐配置
Flutter: 3.24.x
Java: 17 (不要用 21,除非必须)
Gradle: 8.2
AGP: 8.2.1
Kotlin: 1.9.20

配置文件

android/settings.gradle(新版结构):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
pluginManagement {
    repositories {
        google()
        mavenCentral()
    }
}

plugins {
    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
    id "com.android.application" version "8.2.1" apply false
    id "org.jetbrains.kotlin.android" version "1.9.20" apply false
}

include ":app"

android/gradle/wrapper/gradle-wrapper.properties

1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-all.zip

android/app/build.gradle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
android {
    namespace "com.example.app"
    compileSdk 34

    defaultConfig {
        minSdk 21
        targetSdk 34
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }

    kotlinOptions {
        jvmTarget = '17'
    }
}

方案 B:旧版结构迁移到新版

步骤

  1. 备份项目
  2. 升级 Flutter:flutter upgrade
  3. 重新生成 Android 配置:
    1
    2
    3
    4
    
    cd android
    rm -rf build.gradle settings.gradle
    cd ..
    flutter create --platforms=android .
    
  4. 恢复自定义配置(如私有仓库)

七、预防措施和最佳实践

1. 版本锁定

在项目根目录创建 VERSIONS.md

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 项目依赖版本

- Flutter: 3.24.5
- Dart: 3.5.4
- Java: 17.0.9
- Gradle: 8.2
- AGP: 8.2.1
- Kotlin: 1.9.20

## 构建环境

- macOS: 14.0+
- Android Studio: 2023.3.1+

## 最后更新: 2024-12-23

2. 使用 FVM 管理 Flutter 版本

1
2
3
4
5
6
7
8
# 安装 FVM
dart pub global activate fvm

# 锁定项目 Flutter 版本
fvm use 3.24.5

# 使用指定版本运行命令
fvm flutter build apk

3. Docker 化构建环境

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM cirrusci/flutter:3.24.5

# 固定 Java 版本
RUN apt-get update && apt-get install -y openjdk-17-jdk

# 设置环境变量
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH=$JAVA_HOME/bin:$PATH

WORKDIR /app
COPY . .

RUN flutter pub get
RUN flutter build apk --release

4. CI/CD 配置示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# .github/workflows/build.yml
name: Android Build

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-java@v3
        with:
          distribution: 'temurin'
          java-version: '17'

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.24.5'
          channel: 'stable'

      - run: flutter pub get
      - run: flutter build apk --release

5. 团队协作规范

强制使用相同环境

1
2
3
4
# 项目根目录创建 .tool-versions (适用于 asdf)
flutter 3.24.5
java openjdk-17
gradle 8.2

pre-commit hook

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash
# .git/hooks/pre-commit

# 检查 Flutter 版本
FLUTTER_VERSION=$(flutter --version | grep -oP "Flutter \K[0-9]+\.[0-9]+\.[0-9]+")
EXPECTED_VERSION="3.24.5"

if [ "$FLUTTER_VERSION" != "$EXPECTED_VERSION" ]; then
    echo "错误:Flutter 版本不匹配"
    echo "期望: $EXPECTED_VERSION"
    echo "实际: $FLUTTER_VERSION"
    exit 1
fi

八、常见错误速查表

错误信息原因解决方案
jlink error with Java 21AGP < 8.2.1 不支持 Java 21升级 AGP 到 8.2.1+
Cannot resolve dependency缺少仓库配置pluginManagement 添加仓库
Gradle version too oldAGP 需要更新的 Gradle升级 gradle-wrapper.properties
Kotlin plugin versionKotlin 版本与 Gradle 不兼容升级 Kotlin 到 1.9.0+
namespace not specifiedAGP 8.0+ 需要 namespaceapp/build.gradle 添加 namespace
Unsupported class file major versionJava 版本太新降级到 Java 17 或升级 AGP

九、未来展望

Flutter 和 Android 团队正在努力改进:

已实现的改进

  • ✅ 新版项目结构(Flutter 3.16+)
  • ✅ 更友好的错误提示(Flutter Fix)
  • ✅ 自动版本检测警告

计划中的改进

  • 🚧 自动依赖版本协调
  • 🚧 统一的版本管理工具
  • 🚧 更详细的兼容性文档

社区期望

  • 🔮 一键升级命令:flutter upgrade android-deps
  • 🔮 版本冲突自动修复
  • 🔮 可视化依赖管理界面

十、总结

Flutter Android 构建的版本地狱源于:

  1. 多个独立组织的工具链整合
  2. 历史包袱和向后兼容性要求
  3. 构建系统的自举特性
  4. 缺乏统一的版本协调机制

应对策略

  • ✅ 使用稳定的版本组合
  • ✅ 锁定开发环境版本
  • ✅ 理解依赖关系链
  • ✅ 保持配置文件简洁
  • ✅ 记录和分享工作配置

虽然现状不完美,但理解了这些依赖关系后,你就能快速定位和解决问题,而不是在错误信息中迷失方向。

版本推荐

  • Flutter 版本:3.24.x
  • 推荐 AGP:8.2.1
  • 推荐 Java:17 最后的建议:如果配置正常工作,就不要随意升级 — 这不是保守,而是务实 😉

参考资源

2 - Flutter macOS 代码签名问题解决指南

问题背景

在使用 Flutter 开发 macOS 应用时,经常会遇到以下错误:

error: Signing for "Runner" requires a development team.
Select a development team in the Signing & Capabilities editor.

或者:

error: "Runner" requires a provisioning profile.
Select a provisioning profile in the Signing & Capabilities editor.

这是因为 macOS 应用默认需要代码签名才能构建和运行。本文将介绍几种解决方案及其适用场景。

解决方案对比

方案一:使用 Apple ID 签名(推荐用于正式开发)

适用场景

  • 准备发布到 App Store 或进行 TestFlight 测试
  • 需要分发给其他开发者
  • 长期 macOS 开发

优点

  • ✅ 最标准、最官方的做法
  • ✅ 可以使用完整的 macOS 权限和功能
  • ✅ 应用可以正常分发和安装
  • ✅ 免费 Apple ID 即可(无需付费开发者账号)

缺点

  • ❌ 需要 Xcode(约 10-15 GB)
  • ❌ 配置相对繁琐
  • ❌ 需要网络验证

操作步骤

  1. 安装 Xcode(从 App Store)

  2. 在 Xcode 中添加 Apple ID:

    • 打开 Xcode → Settings → Accounts
    • 点击 + 添加 Apple ID
    • 登录后会自动创建开发证书
  3. 配置项目签名:

    1
    
    open macos/Runner.xcworkspace
    
    • 选择 Runner 项目 → Runner target
    • 进入 Signing & Capabilities
    • 勾选 “Automatically manage signing”
    • 选择你的团队
  4. 构建运行:

    1
    
    flutter run -d macos
    

方案二:创建自签名证书

适用场景

  • 不想使用 Apple ID
  • 只在自己电脑上开发测试
  • 已安装 Xcode Command Line Tools

优点

  • ✅ 不需要 Apple ID
  • ✅ 无需网络验证
  • ✅ 支持基本的应用功能

缺点

  • ❌ 应用无法分发给他人
  • ❌ 某些系统功能可能受限
  • ❌ 需要手动管理证书

操作步骤

  1. 创建自签名证书:

    • 打开"钥匙串访问"(应用程序 → 实用工具)
    • 钥匙串访问 → 证书助理 → 创建证书
    • 名称:Mac Developer
    • 身份类型:自签名根证书
    • 证书类型:代码签名
    • 勾选"让我覆盖这些默认值"
    • 一路继续,最后点击创建
  2. 验证证书:

    1
    
    xcrun security find-identity -v -p codesigning
    
  3. 在 Xcode 中配置项目使用该证书(步骤同方案一)

方案三:完全禁用代码签名(推荐用于本地开发)

适用场景

  • 纯本地开发测试
  • 不需要分发应用
  • 不想安装 Xcode
  • 快速迭代开发

优点

  • ✅ 无需任何证书或 Apple ID
  • ✅ 无需安装 Xcode
  • ✅ 配置简单快速
  • ✅ 适合 CI/CD 环境

缺点

  • ❌ 应用无法分发(需要用户手动处理权限)
  • ❌ 某些系统功能可能受限
  • ❌ 不符合 Apple 官方规范

操作步骤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash
FILE="macos/Runner.xcodeproj/project.pbxproj"

perl -i -pe '
  s/CODE_SIGN_ENTITLEMENTS = .*?;/CODE_SIGN_ENTITLEMENTS = "";/g;
  s/CODE_SIGN_IDENTITY = .*?;/CODE_SIGN_IDENTITY = "";/g;
  s/CODE_SIGN_STYLE = .*?;/CODE_SIGN_STYLE = Manual;/g;
  s/DEVELOPMENT_TEAM = .*?;/DEVELOPMENT_TEAM = "";/g;
  s/PROVISIONING_PROFILE_SPECIFIER = .*?;/PROVISIONING_PROFILE_SPECIFIER = "";/g;
' "$FILE"

echo "✅ Signing configuration disabled"

应用分发说明

如果选择方案三(禁用签名)进行开发,在分发应用时用户需要执行以下操作才能运行:

方法 1:移除隔离属性(推荐)

1
xattr -rd com.apple.quarantine /Applications/YourApp.app

注意:不需要使用 sudo,普通用户权限即可。

方法 2:右键打开

  1. 右键点击应用
  2. 选择"打开"
  3. 在弹出的对话框中点击"打开"

这种方法可以绕过 Gatekeeper 的检查,但只需要操作一次。

推荐方案总结

场景推荐方案理由
本地快速开发方案三(禁用签名)配置简单,开发效率高
团队协作开发方案一(Apple ID)标准流程,便于协作
准备发布应用方案一(Apple ID)必须使用正式签名
无 Xcode 环境方案三(禁用签名)不依赖 Xcode
CI/CD 自动化方案三(禁用签名)减少外部依赖

常见问题

Q: 为什么会出现 “0 valid identities found”?

A: 这表示系统中没有可用的代码签名证书。可以选择:

  1. 在 Xcode 中登录 Apple ID 自动创建
  2. 手动创建自签名证书
  3. 完全禁用签名(方案三)

Q: entitlements 文件的作用是什么?

A: entitlements 文件定义了应用的权限,例如:

  • app-sandbox: 是否启用沙盒
  • allow-jit: 是否允许 JIT 编译(Flutter 需要)
  • network.client/server: 网络访问权限

Q: 禁用签名后应用安全吗?

A: 对于本地开发完全安全。但分发给用户时:

  • 用户需要手动信任应用
  • 系统会显示"来自身份不明开发者"的警告
  • 不适合公开分发

Q: 如何在开发和发布之间切换?

A:

  • 开发时使用方案三(禁用签名)
  • 发布前切换到方案一(Apple ID 签名)
  • 可以通过 Git 管理不同配置

结论

对于日常开发,方案三(完全禁用签名) 是最高效的选择,无需额外工具,配置简单。当需要正式发布时,再切换到方案一(Apple ID 签名)

选择合适的方案可以大大提升开发效率,避免在签名问题上浪费时间。


参考资源

3 - 解决 Flutter 插件在 Android Studio 中的 Gradle 版本冲突

问题现象

在 Android Studio 中打开 Flutter 插件的 Android 目录时,遇到以下错误:

Build file '~/libpag/flutter-ext/android/build.gradle' line: 23
A problem occurred evaluating root project 'libpag_flutter'.
> 'org.gradle.api.artifacts.Dependency org.gradle.api.artifacts.dsl.DependencyHandler.module(java.lang.Object)'
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/9.0-milestone-1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

问题原因

这是典型的 Gradle 版本与 Android Gradle Plugin (AGP) 版本不兼容问题。

在 Android 项目中,有两个关键版本需要匹配:

  1. Gradle 版本:构建工具本身(如 7.5, 8.0, 9.0)

    • 定义在 gradle/wrapper/gradle-wrapper.properties
  2. AGP 版本:Android 专用插件

    • 定义在 build.gradleclasspath 'com.android.tools.build:gradle:x.x.x'

版本兼容关系:

Gradle 版本AGP 版本
7.4 - 7.67.3.0 - 7.4.2
8.0 - 8.38.0.0 - 8.1.4
8.4+8.2.0+

Flutter 插件的特殊性

Flutter 插件项目通常没有 gradle-wrapper.properties 文件,因为它依赖宿主应用的 Gradle 配置。

但在 Android Studio 中直接打开插件的 android 目录时,IDE 会将其视为独立项目,使用默认 Gradle 版本(可能是 9.0),导致与旧版 AGP 冲突。

解决方案

方案一:创建 gradle-wrapper.properties(推荐)

android/gradle/wrapper/ 目录下创建 gradle-wrapper.properties

1
2
3
4
5
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

方案二:升级 AGP 版本

修改 android/build.gradle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
buildscript {
    ext.kotlin_version = '1.9.0'
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath 'com.android.tools.build:gradle:8.1.4'  // 升级到 8.1.4
    }
}

allprojects {  // 注意:去掉 rootProject
    repositories {
        google()
        mavenCentral()
    }
}

方案三:在 Android Studio 中配置

  1. 打开 File → Settings → Build Tools → Gradle
  2. 选择 Use Gradle from: ‘specified location’
  3. 指定或下载 Gradle 7.5

建议

对于 Flutter 插件开发,推荐方案一,改动最小且不影响插件在实际 Flutter 项目中的使用。

执行任一方案后,点击 Android Studio 的 Sync Now 重新同步项目即可。