1. 动手之前先画图纸:一套能跑 Android 的 Flutter 环境到底由什么组成
Flutter 开发环境搭建这件事,说起来就一句“装个 SDK 配个 Android”,但真到 Windows 上从零开始,多数人第一次会卡在第三天:flutter doctor一屏幕红叉,Gradle 卡在Running Gradle task 'assembleDebug'...十几分钟不动,模拟器启动报 HAXM 失效。我自己第一次搭这套环境时,来回折腾了两个晚上,原因并不在于哪个步骤难,而在于不知道每个组件之间的依赖关系,出问题时只能瞎试。所以这篇内容我想换个讲法,先讲清楚这套东西的骨架长什么样,再带你一步步落地,最后把那些官方文档里不会写的坑全摊开。适合刚上手 Flutter、想在 Windows 上跑通 Android 真机或模拟器的人,也适合已经把环境装好但构建慢得离谱、想回头优化的人。
先把话说前面:Flutter 在 Windows 上做 Android 开发,本质是“一套 Dart 工具链 + 一套 Android 原生构建链 + 一个 IDE”的三层结构。Flutter SDK 负责 Dart 编译和把 Flutter 代码打成 Android 工程,Android SDK 负责把那个工程编译成 APK,Gradle 负责整个 Android 侧的依赖解析和构建编排。三层里任意一层版本对不上,报错就出现在别的地方,让人误判。搞懂这个分层,后面排查问题会快很多。
1.1 四个必备件与两个可选件
必备件只有四个:Flutter SDK、Android SDK(含 platform-tools 和 build-tools)、一个能跑 Android 构建的 JDK、一个编辑器(Android Studio 或 VS Code 都行,纯命令行也能干活)。可选件是两个:Android 模拟器(AVD)和 Git。Git 严格说也算必备,因为 Flutter 的pub依赖很多来自 Git 仓库,而且你迟早要把代码托管出去,我一般直接算进必备。
很多人一上来装 Android Studio 的完整包,勾上一堆组件,装完十几 G,结果真正被 Flutter 用到的只有几个。我把常用组件的实际用途列一下,你就知道自己该勾什么了:
| 组件 | 是否必需 | 实际作用 | 不装的后果 |
|---|---|---|---|
| Flutter SDK | 必需 | Dart 编译、Flutter 框架、flutter命令本身 | 无从谈起 |
| platform-tools(adb) | 必需 | 与真机/模拟器通信、安装 APK | 设备列表永远为空 |
| build-tools | 必需 | 打包 APK、AAPT 资源处理 | 构建到最后一步失败 |
| platforms;android-34(版本随目标而定) | 必需 | 提供 compileSdk 对应的 android.jar | Gradle 报找不到 platform |
| cmdline-tools;latest | 强烈建议 | sdkmanager、avdmanager命令行入口 | flutter doctor报 cmdline-tools 缺失 |
| emulator + system-image | 可选 | 图形化模拟器 | 只能真机调试 |
| NDK | 通常不需要 | 只有含原生库的插件才需要 | 一般不影响 |
| CMake | 通常不需要 | 同上 | 一般不影响 |
这张表是我踩坑之后整理出来的。早期我按“全勾上总没错”的思路装,结果 NDK 装了三份,AGP 每次构建都提示ndkVersion不匹配,反而多了一堆噪音。现在的做法是只装必需项加模拟器,NDK 等真遇到插件需要再补,工程里写死ndkVersion明确版本,反而更省心。
1.2 版本组合的自我克制:为什么我不建议全用最新
Flutter、Dart、AGP、Gradle、JDK、Kotlin 这六个版本是联动的,不是各自独立。你装的 Flutter 版本决定了它能识别的 AGP 上限,AGP 又决定需要的 JDK 版本和 Gradle 版本,Kotlin 插件版本还要跟 AGP 对齐。全用最新版往往是最不稳的组合,因为新版 Flutter 默认生成的模板工程是按某个 AGP 范围测过的,而你的 Android Studio 可能顺手把 AGP 升到了它没测过的版本。
一个相对稳的搭配思路是这样的:Flutter stable 渠道的次新版本(不是当天发布的最新)、AGP 8.x、Gradle 8.x、JDK 17、Kotlin 1.9.x 系列。JDK 17 是这一段的硬门槛,因为 AGP 8 起最低要求就是 17,你要是拿 JDK 8 去跑,会得到一个非常不直观的报错,提示里根本不会出现“JDK 版本太低”这几个字。对应的关系我整理成了表,装之前对一眼,能省掉后面一半的排查时间:
| 组件 | 推荐区间 | 关键约束 |
|---|---|---|
| Flutter | stable 次新版 | 与 Dart 版本强绑定,不能单独换 Dart |
| JDK | 17 | AGP 8.x 的最低要求,低于此直接失败 |
| Gradle | 8.x | 与 AGP 版本配对,模板工程写好就别动 |
| AGP | 8.x | 决定 compileSdk 上限和 JDK 门槛 |
| Kotlin | 与模板一致 | 随意升级容易触发元数据版本冲突 |
| compileSdk | 34 或模板默认 | 插件可能要求更高 |
我这里没写具体的小版本号,原因是这些数字半年就变一轮,写死了反而误导。判断方法很简单:新建一个空项目,先不动任何版本跑通一次,把android/build.gradle和gradle-wrapper.properties里的版本记下来,这就是你当前这套环境“官方验证过”的组合,之后所有升级都在这个基准上小步试。
1.3 目录规划这件小事,决定了你后面 80% 的报错
这一条我放在最前面讲,因为它最不值钱也最容易忽略。三条规矩:路径全英文、路径尽量短、不要让任何工具目录落在用户目录下的中文名下。
Windows 上 Flutter 和 Gradle 的很多问题都跟路径有关。中文用户名会导致pub缓存路径里出现非 ASCII 字符,部分插件在打包资源时会失败;路径过长会撞上 260 字符限制,Gradle 生成中间文件时直接报“文件名或扩展名太长”;带空格的路径在少数脚本里没加引号,命令直接被拆断。我的做法是统一放在一个短路径下:
D:\dev\flutter <- Flutter SDK 解压位置 D:\dev\android-sdk <- Android SDK 根目录 D:\dev\jdk17 <- JDK D:\work\projects <- 所有项目这条规矩看着土,但效果很直接。我帮别人看环境问题,十次里有三次是路径里带了中文或者空格,改完路径问题当场消失。另外提一句,解压 Flutter SDK 千万别解压到C:\Program Files下面,那里对普通进程有写保护,flutter precache下载缓存时会失败,而且报错信息极其含糊。
提示:Windows 系统的长路径支持建议一并打开,在系统的组策略或注册表里启用
LongPathsEnabled。Flutter 工程嵌套层级本来就深,第三方插件再套几层,很容易超过限制。
2. 安装顺序与选型逻辑:为什么不能想到哪装到哪
安装顺序不是随便定的,它由依赖关系决定。JDK 是 Android 构建的底座,Android SDK 的sdkmanager本身也需要 JDK 才能跑;Flutter SDK 装好之后第一件事就是去找 Android SDK,找不到就报错;编辑器放最后,因为它只是个壳,负责给你提供代码提示和运行按钮。我见过有人先装 Android Studio,然后在里面点“安装 Flutter 插件”,插件的安装向导又去下 Flutter SDK,下完发现 JDK 版本不对,整套重来。顺序错了不是不能修,是修起来四处补丁。
2.1 依赖关系决定顺序
按我的习惯,顺序是:JDK → Android SDK(命令行方式)→ Flutter SDK → 环境变量 → 编辑器 → 模拟器/真机。中间“环境变量”单独作为一步,是因为它必须在 Flutter 和 Android SDK 都到位之后配,配早了没意义,配晚了所有命令都找不到。
这里有个取舍值得说一下:Android SDK 是用 Android Studio 的图形界面向导装,还是用sdkmanager命令行装?我偏向命令行。图形向导装的东西多、位置不好控制,而且它在升级时经常顺手改你的 SDK 目录结构。命令行装的好处是可控,装了什么一目了然,换机器时把命令复制一遍就完事。Android Studio 你照样可以装,但只把它当编辑器和 AVD 管理器用,不让它管 SDK。
具体做法是:单独下一个cmdline-tools包,解压后放到D:\dev\android-sdk\cmdline-tools\latest\,注意目录结构必须是cmdline-tools\latest\bin\sdkmanager.bat,多套一层或少套一层都会导致flutter doctor找不到它。这个目录层级是硬要求,我吃过一次亏,解压成了cmdline-tools\cmdline-tools\bin,排查了半小时。
2.2 JDK 选型:从 8 到 17 的迁移是一次性成本
老项目多的人机器上往往躺着 JDK 8,那是当年 Android 构建的标配。现在不行了,AGP 8 起明确要求 JDK 17。我的建议是机器上可以留多个 JDK,但JAVA_HOME指向 17,工程里如果确实有需要 8 的老项目,在那个项目自己的gradle.properties里单独指定org.gradle.java.home,不要全局来回切。
装完先验证,这一步别省:
java -version javac -version echo %JAVA_HOME%三条命令要指向同一个 JDK 目录,JAVA_HOME应该是 JDK 根目录而不是bin目录,这是最常见的错。如果java -version显示 8 而JAVA_HOME是 17,说明 Path 里有个更靠前的 java.exe,八成是当年装 Oracle JDK 时自动塞进去的C:\ProgramData\Oracle\Java\javapath,把它从 Path 里挪走或者删掉。
另外一个容易被忽略的点:Android Studio 自带一个 JetBrains Runtime,那个 JBR 是给 IDE 自己用的,不一定适合当项目的构建 JDK。有些老版本 Flutter 会去 IDE 目录里找 JDK,找不到就报Unable to find bundled Java version,那是历史遗留问题,新版基本不出现了。稳妥做法是让命令行构建用系统 JDK 17,别依赖 IDE 自带的。
2.3 Android SDK 只装这五样
环境变量配好之后,用sdkmanager装组件,一条命令搞定:
sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0" "cmdline-tools;latest" "emulator"装之前可以先sdkmanager --list看一眼有哪些可用版本,注意platforms和build-tools的版本号要跟你在工程里写的compileSdk、buildToolsVersion对得上。Flutter 模板工程一般会自动推导,但如果你手动改过compileSdk,就得保证对应 platform 已经装了,否则 Gradle 会报找不到 android.jar。
许可协议这一步很多人漏掉,表现为flutter doctor里 Android toolchain 那一项一直是黄色警告,写着Android license status unknown。解决办法就是跑一次:
flutter doctor --android-licenses一路按y回车。如果这条命令报sdkmanager not found,说明cmdline-tools\latest\bin没进 Path,回去补环境变量。
注意:许可协议是按 SDK 目录记录的,换了 SDK 路径就要重新接受一次。团队里有人把 SDK 从 C 盘挪到 D 盘之后构建全挂,就是这个原因。
3. 从零到 flutter doctor 全绿:我的一次完整实操记录
这一节我把实际动手的过程按顺序写一遍,包括我当时执行的命令和看到的结果。你照着做,中间可能会遇到跟我不同的报错,那就跳到第 5 节的速查表对照。
3.1 环境变量:四个变量和一个 Path
系统环境变量里加这几个,注意是“系统变量”而不是“用户变量”,避免切换用户后失效:
| 变量名 | 值 | 说明 |
|---|---|---|
JAVA_HOME | D:\dev\jdk17 | JDK 根目录,不是 bin |
ANDROID_HOME | D:\dev\android-sdk | Android SDK 根目录 |
ANDROID_SDK_ROOT | 同上 | 部分老工具仍在读这个 |
FLUTTER_STORAGE_BASE_URL | 公共镜像地址 | 加速 Flutter 产物下载 |
PUB_HOSTED_URL | 公共镜像地址 | 加速 pub 依赖下载 |
后两个是可选但强烈建议的,具体地址以官方文档当前列出的为准,我这里不写死是因为镜像地址会变,抄一个过期的反而添乱。配了这两个之后flutter pub get的速度差别非常明显,尤其是在拉一些依赖比较多的项目时。
Path 里要加三条,顺序有讲究:
D:\dev\flutter\bin %ANDROID_HOME%\platform-tools %ANDROID_HOME%\cmdline-tools\latest\binflutter\bin放最前面,避免机器上装了多个 Flutter 版本时命令指向混乱。加完之后一定要重开一个命令行窗口再验证,老窗口读的是旧环境变量,这是新手最容易怀疑人生的地方——明明配好了,flutter就是提示不是内部或外部命令。
验证方式:
flutter --version adb version sdkmanager --version三条都有输出,环境变量这关就过了。
3.2 镜像与网络:pub get 和 Gradle 各自的卡点
这里要分清两条完全不同的下载链路,很多人混为一谈,导致排查方向跑偏。
第一条是 Flutter 和 Dart 的链路:flutter precache、flutter pub get。这条链路由PUB_HOSTED_URL和FLUTTER_STORAGE_BASE_URL控制,配一次全局生效,前提是重开终端。判断有没有生效,看flutter pub get的输出里下载地址是不是你配的镜像。
第二条是 Android 构建的链路:Gradle 发行包、Maven 依赖、AGP 插件。这条链路完全不受上面两个变量影响,它由android/build.gradle里的repositories和gradle/wrapper/gradle-wrapper.properties里的distributionUrl决定。很多人配了 pub 镜像发现构建还是慢,就是因为这条链路根本没动。
Gradle 发行包的地址在项目里:
# android/gradle/wrapper/gradle-wrapper.properties distributionUrl=https\://services.gradle.org/distributions/gradle-8.x-bin.zip这个地址如果访问慢,可以换成公共镜像上对应的同一版本包。关键是版本号必须一致,换成不同版本会触发 AGP 兼容性报错,报错信息里一堆Unsupported class file major version,看着吓人。
Maven 仓库同理,在android/build.gradle的buildscript.repositories和allprojects.repositories两个位置都要加,顺序放在最前面,让它优先生效:
buildscript { repositories { maven { url '公共镜像地址' ; allowInsecureProtocol = false } mavenCentral() } } allprojects { repositories { maven { url '公共镜像地址' } mavenCentral() } }两个位置都要加,是因为一个管插件依赖,一个管应用依赖,只加一个会出现“插件下得来、库下不来”的诡异现象。
3.3 flutter doctor 红色条目逐条修复
flutter doctor是整个流程里最有价值的诊断工具,但它输出的条目太多,容易让人以为全都要绿。实际上做 Windows + Android 开发,你只需要关心Flutter、Android toolchain、Connected device这三项。Visual Studio是 Windows 桌面开发要用的,Chrome是 Web 开发要用的,这两个红着完全不影响你跑 Android。
我当时的输出和修复动作是这样的:
cmdline-tools component is missing,说明cmdline-tools目录没装或者层级不对,回去检查cmdline-tools\latest\bin\sdkmanager.bat是否存在。
Android license status unknown,跑flutter doctor --android-licenses。
Unable to locate Android SDK,两种修法:要么补ANDROID_HOME,要么直接告诉 Flutter 路径,flutter config --android-sdk D:\dev\android-sdk。后者写进 Flutter 自己的配置文件,优先级比环境变量高,适合 SDK 放在非标准位置的情况。
Building with plugins requires symlink support,这条在 Windows 上几乎人人都会遇到。解决方法是在系统的开发者选项里打开“开发人员模式”,Flutter 需要创建符号链接来管理插件。不开的话,带插件的项目构建会失败,纯 Dart 项目反而没事,所以很多人是写到第二个项目才撞上。
No devices found,正常,装完模拟器或者插上手机就绿了。
一条经验:不要为了追求flutter doctor全绿去装 Visual Studio 那一大坨,二十多 G 的下载量,对你跑 Android 一点用没有。
3.4 模拟器与真机:两条路都留一手
模拟器的好处是随时可用,坏处是吃资源、启动慢、某些传感器行为跟真机不一致。真机的好处是真实,坏处是要插线、要装驱动。我的建议是两条都备着,日常用真机,需要多尺寸适配时开模拟器。
模拟器现在推荐用系统虚拟化方案,不再用早年那个 HAXM。前提是 BIOS 里开了虚拟化(VT-x 或 AMD-V),系统里启用“虚拟机监控程序平台”和“虚拟机平台”两个功能,重启生效。这两步没做的话,AVD 启动会报一个跟 CPU 加速有关的错误,文字很长但核心就是“虚拟化不可用”。
系统镜像选择上,选 x86_64 的镜像在 Windows 上跑得比 arm 镜像快得多,因为不需要指令翻译。版本选跟你compileSdk接近的那个,别选太老的,老镜像里缺一些新 API,测试时会出现“明明代码没问题但运行时报方法找不到”的怪事。
真机这边,常见的是插上手机adb devices显示unauthorized。这不是故障,是手机端没点“允许 USB 调试”的授权弹窗。如果弹窗一直不出现,在手机的开发者选项里点一下“撤销 USB 调试授权”,然后重新插拔。还有一种情况是显示offline,adb kill-server再adb start-server一般能解决。部分品牌手机需要额外打开“USB 安装”权限,否则装 APK 那一步会静默失败。
3.5 第一个项目跑起来,以及首次构建为什么慢
到这里就可以建项目了:
flutter create --org com.example --project-name demo_app demo_app cd demo_app flutter devices flutter run -d <设备ID>第一次flutter run会非常慢,五到十五分钟都正常,因为 Gradle 要下载发行包、下载 AGP、下载一堆 Maven 依赖、下载 Flutter 的 embedding 产物、编译一遍 Android 工程、再编译 Dart 到 AOT/JIT。这个阶段最忌讳的就是看到卡住就 Ctrl+C 重来,重来一次前面的部分缓存白下,反而更慢。
判断是不是真的卡死,看两个地方:一是任务管理器里有没有 Java 进程在吃 CPU 和网络,有就说明在干活;二是android/.gradle和用户目录下的.gradle\caches目录大小有没有在涨。两个都在动,就耐心等。
跑通之后,第二次构建通常在 20 秒到 1 分钟之间,热重载几乎是秒级。这个对比很能说明问题:慢的不是 Flutter,是首次的依赖拉取。
4. 构建速度调优:Gradle 这一段最容易被忽略
环境能跑通只是及格线,日常开发里真正消耗你时间的是构建。我统计过自己一个中等规模项目的构建耗时,冷启动十几分钟,改一行代码热重载 1 秒,改原生配置后重建 40 秒到 2 分钟。要优化的是后面那个数字,因为热重载已经足够快了,反而是每次动build.gradle、加插件、切分支之后的全量重建在吃时间。
4.1 首次构建的时间都花在哪
拆开看,首次构建的时间大致分布在四块:Gradle 发行包下载、Maven 依赖下载、Flutter 引擎产物下载、实际编译。前三块都是网络 IO,第四块才是 CPU。这意味着如果你的网络条件一般,优化方向应该是“下载一次,永久缓存”,而不是去调编译器参数。
对应的手段有三个。一是把 Gradle 的全局缓存目录保留好,默认在用户目录的.gradle下,别没事清它。二是 Flutter 的引擎产物用flutter precache --android提前拉全,这个命令会把 Android 相关的产物一次性下好,之后建新项目就不用重复下。三是让pub缓存也留着,路径在用户目录的AppData\Local\Pub\Cache,某些清理软件会把它当垃圾清掉,加个白名单。
4.2 gradle.properties 参数与它们的意义
工程级的android/gradle.properties是调优的主战场。默认模板里只有寥寥几行,我一般会补成下面这样:
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.caching=true org.gradle.daemon=true android.useAndroidX=true android.enableJetifier=false android.nonTransitiveRClass=true kotlin.incremental=true逐个说下为什么。-Xmx4096m是给 Gradle 守护进程的堆上限,太小会OutOfMemoryError,太大会跟 IDE 抢内存导致整机卡顿,一般取物理内存的四分之一到三分之一比较合适,16G 内存的机器给 4G 是舒服的区间。MaxMetaspaceSize是给类元数据的,插件多的项目不给会报 metaspace 溢出。-Dfile.encoding=UTF-8是防中文乱码的,Windows 默认编码不是 UTF-8,某些资源文件处理时会出问题。
parallel开启并行构建,多模块项目受益明显。caching开启构建缓存,同样的输入直接复用上次输出,切分支之后效果肉眼可见。daemon保持守护进程常驻,省掉每次构建启动 JVM 的开销,这个默认就是开的,但有人为了“干净”手动关掉,反而更慢。
enableJetifier=false这条值得单独说。Jetifier 是 AndroidX 迁移期的兼容工具,作用是把旧的支持库引用改写成 AndroidX,代价是构建时扫描所有依赖,非常耗时。现在的依赖基本都是 AndroidX 了,关掉它能省下可观的构建时间。但如果你的项目里有很老的第三方库,关掉会编译失败,那就只能留着。
4.3 仓库配置与离线缓存
仓库顺序影响的不只是速度,还有稳定性。把响应快的镜像放前面,官方仓库放后面兜底,这样绝大多数依赖走镜像,个别镜像没有的还能从官方拿到。这个配置要写在两个地方,前面提过,不再重复。
离线模式是应急手段,flutter build apk --offline或者 Gradle 加--offline,前提是依赖已经全部缓存过。适合在没网的环境下做演示,不适合日常,因为一旦有新依赖就会失败,报错信息也不直观。
顺便说一个小技巧:如果你经常在不同项目之间切换,且它们用的 AGP 版本不一样,Gradle 会为每个版本各起一个守护进程,内存占用会叠加。可以在~/.gradle/gradle.properties里统一限制守护进程数量和堆大小,避免开五个项目把内存吃满。
4.4 杀毒软件与索引服务
这一条容易被忽略但影响巨大。Windows 上默认的实时防护会扫描每一个新生成的文件,而 Gradle 构建过程会生成成千上万个中间文件,扫描开销非常可观。我实测过,把 Flutter SDK 目录、Android SDK 目录、Gradle 缓存目录、项目目录四个位置加进排除列表,冷构建时间能缩短三成左右。
具体加哪几个目录:
D:\dev\flutterD:\dev\android-sdkC:\Users\<用户名>\.gradleC:\Users\<用户名>\AppData\Local\Pub\Cache- 你的项目根目录
除了杀毒软件,系统的文件索引服务也会拖慢构建。项目目录一般不需要被搜索索引,关掉对那个目录的索引即可。这两个调整加起来,收益比调 JVM 参数还大,而且没有任何副作用。
注意:把目录加进排除列表是降低防护范围的操作,只对你完全信任的开发目录做,不要把整个磁盘加进去。
5. 报错速查:我在 Windows 上踩过的那些坑
下面这张表是我这几年攒下来的,覆盖了环境搭建阶段八成以上的报错。表格只能给方向,具体还是要看完整报错信息,因为同一个现象可能有好几种成因。
5.1 环境识别类
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
flutter不是内部或外部命令 | Path 未配或未重开终端 | 补flutter\bin到 Path,重开窗口 |
Unable to locate Android SDK | ANDROID_HOME缺失或路径错 | 补变量,或用flutter config --android-sdk |
cmdline-tools component is missing | 目录层级不对 | 保证是cmdline-tools\latest\bin\sdkmanager.bat |
Android license status unknown | 未接受许可 | flutter doctor --android-licenses |
Unable to find bundled Java version | IDE 自带 JBR 路径问题 | 让命令行用系统 JDK 17,别依赖 IDE |
Waiting for another flutter command to release the startup lock | 上次命令异常退出留下锁文件 | 删除flutter\bin\cache\lockfile |
最后那条我要多说两句。这个锁文件是 Flutter 用来防止并发执行的,正常退出会自动清理,但如果命令被强制中断(比如你在pub get时按了 Ctrl+C),锁就留下了,之后所有flutter命令都会卡在这句话上。很多人以为是网络问题,实际就是删个文件的事。
5.2 构建失败类
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
卡在Running Gradle task 'assembleDebug'...很久 | 首次下载依赖 | 看网络和缓存目录是否在增长,耐心等 |
Unsupported class file major version | JDK 与 Gradle/AGP 不匹配 | 统一到 JDK 17 |
Could not resolve io.flutter:... | Flutter 引擎产物下载失败 | 检查FLUTTER_STORAGE_BASE_URL,flutter precache --android |
Could not find method implementation() | 依赖写在buildscript块里了 | 移到dependencies块 |
AAPT2 error/ 资源编译失败 | 路径含中文或空格、资源命名不合规 | 全英文短路径,资源名只用小写字母下划线 |
minSdkVersion冲突 | 某插件要求更高版本 | 在android/app/build.gradle提高minSdk |
Duplicate class | 依赖里同一个库有两个版本 | 用exclude或dependencyResolutionManagement锁定 |
文件名或扩展名太长 | 超过 260 字符限制 | 开启长路径支持,缩短项目路径 |
Could not resolve io.flutter这个报错我印象最深。当时配了 pub 镜像,pub get飞快,但构建就是失败在这行,查了很久才明白:Flutter 引擎的 Android 产物走的是FLUTTER_STORAGE_BASE_URL这条链路,跟我配的 Maven 镜像完全是两回事。修法就是把这个变量补上,或者跑一次flutter precache --android提前下好。
还有一种“假失败”值得提:构建报错指向某个插件的build.gradle,但那个插件你没动过。这种情况九成是版本不兼容,插件声明的 AGP 版本比你项目的低。修法不是改插件代码,而是在项目的android/build.gradle里把 AGP 降到你所有插件都支持的最低版本,或者去升插件版本。改第三方代码是最差的选择,下次升级就没了。
5.3 运行与设备类
| 现象 | 大概率原因 | 处理方式 |
|---|---|---|
adb devices显示unauthorized | 手机未授权 | 手机端点允许,或撤销授权重插 |
设备显示offline | adb 服务状态异常 | adb kill-server后start-server |
| 模拟器启动报虚拟化相关错误 | BIOS 虚拟化未开或系统功能未启 | 开 VT-x/AMD-V,启用虚拟机平台并重启 |
| 应用装上但闪退 | 通常是原生层崩溃 | flutter run看 logcat 输出,注意E/AndroidRuntime |
| 热重载不生效 | 改动涉及原生或main() | 用热重启R,必要时全量重跑 |
| 安装 APK 失败提示空间不足 | 模拟器磁盘镜像太小 | 重建 AVD,调大存储 |
las 那条是新手常问的:改了点main()之外的东西,热重载r没反应。原因是热重载只替换 Dart 代码的增量,涉及main()、全局初始化、原生代码的改动必须热重启R甚至全量重跑。记住一条经验:改 UI 用r,改初始化逻辑用R,改android/下的任何文件用全量重跑。
6. 多版本共存与团队一致性
环境搭好之后,很快就会遇到第二个问题:手上三个项目,分别依赖不同版本的 Flutter,升级其中一个就崩另外两个。这个不是小概率事件,Flutter 的版本迭代节奏不慢,跨两三个版本 API 变化就出来了。
6.1 多个 SDK 目录 + 切换的思路
最朴素也最可靠的办法是机器上放多份 Flutter SDK,比如D:\dev\flutter-3.19、D:\dev\flutter-3.22,用的时候切 Path 或者用绝对路径调用。缺点是每次切换要改环境变量、重开终端,容易忘。
稍微好一点的做法是写两个批处理脚本,一个切版本号,一个验证:
:: use-flutter.bat @echo off setx PATH "D:\dev\flutter-3.22\bin;%PATH%" echo Flutter switched, please reopen terminal.说实话,这个方案土,但它不引入额外依赖,出问题好查。相比之下,版本管理工具更优雅一些,可以按项目目录自动切换版本,在项目根目录放一个版本声明文件即可。代价是多一个工具要维护,团队里每个人都得装,新人上手成本增加。
我的选择是:个人机器用多目录 + 脚本,团队协作环境用版本管理工具,因为团队更怕“我这里能跑你那里不能跑”这种问题,多一个工具换一致性是划算的。
6.2 版本管理工具的角色
这类工具的核心作用不是“帮你下载版本”,而是“把版本写进项目”。项目根目录的那一个版本声明文件,进版本库之后,任何人克隆下来执行一次命令就自动拿到对应版本,这才是它真正的价值。版本号从“口头约定”变成“仓库里的一个文件”,这个变化对协作的意义远大于省下的那点手动操作。
需要提醒的是,这类工具本身也有版本,团队成员用的工具版本最好统一。曾经遇到过有人用的旧版本工具不认新的版本文件格式,静默用了本地默认版本,构建出一堆奇怪的问题,排查了半天才发现是版本不对。
6.3 把环境写成可复制的文档
环境这件事,写在脑子里等于没写。我现在的习惯是每个项目根目录放一个SETUP.md,内容就三块:依赖的 JDK、Flutter、AGP 版本,装完之后flutter doctor应该长什么样,以及一条从零到跑起来的命令序列。新同事照着做,顺利的话半小时能跑通第一个界面。
这个文档的价值在半年后体现最明显。半年后你自己换电脑,或者要加一个新人,不用回忆当年是怎么配的,照文档抄一遍就行。而且写文档的过程会逼你把之前瞎试出来的配置梳理清楚,很多隐含依赖在这个过程中就暴露了。
7. 手工清单与验收标准
最后给两份清单,一份是装完之后的验收清单,一份是换机重装时的最短路径。
7.1 一次装完的检查清单
按顺序过一遍,全过就说明环境健康:
| 检查项 | 命令 | 期望结果 |
|---|---|---|
| Flutter 可用 | flutter --version | 输出 stable 版本号,无警告 |
| JDK 正确 | java -version | 显示 17 |
| Android 工具链 | adb version | 输出版本号 |
| SDK 管理器 | sdkmanager --version | 输出版本号,不报 JAVA_HOME 错 |
| 诊断全绿 | flutter doctor | 三项关键项绿 |
| 设备可见 | flutter devices | 至少一个设备 |
| 新建项目可跑 | flutter create+flutter run | 界面成功显示 |
| 热重载有效 | 改一行文字按r | 界面秒变 |
表格里最容易出问题的是第五项和第七项。第五项前面已经说过,只看三项。第七项要注意的是首次运行慢是正常的,别因为它慢就以为环境坏了。
7.2 换机或重装时的最短路径
换机器这件事我做过几次,现在能在一小时内恢复。顺序是:装 JDK 17 → 解压 Flutter SDK → 装cmdline-tools→ 配五个环境变量(含两个镜像变量)→sdkmanager装五个组件 →flutter doctor --android-licenses→ 开开发者模式 →flutter doctor验收。模拟器可以等真正需要时再建,反正真机调试更快。
我自己在实际操作里体会最深的一点是:这套环境的复杂度不在某一步难,而在于步骤之间的依赖太隐晦,报错信息又往往指向别处。所以真正省时间的做法不是记住每条命令,而是记住分层——Flutter 层的问题去查flutter doctor,Android 层的问题去查sdkmanager和 Gradle 日志,网络层的问题分清是 pub 链路还是 Maven 链路。把这三层分开看,Windows 上搭 Flutter 的 Android 环境,其实比想象中顺。