说实话,我最早真没把 fvm 当回事。Flutter 装好,PATH 配上,全局往那一指,哪个项目不是跑?直到有一天我需要同时维护两个项目:一个停在 Flutter 3.13,团队要求锁版本不乱动;另一个要试 Flutter 3.22 的新特性,三天两头得跟上小版本。全局 SDK 换来换去,光是来回改 PATH、重新跑 flutter doctor 就耗掉不少时间,更别提一升级就把老项目的编译环境冲掉。fvm(Flutter Version Management)就是来解决这个问题的,在 Windows 下它尤其好用,因为它不像 macOS/Linux 那样有很多系统级 SDK 管理的天然优势,反而更需要一个显式工具把多版本 Flutter 管起来。
这篇文章不聊 fvm 的源码,也不讲什么花活,就讲 Windows 上从零装 fvm、日常怎么用、版本升级和回退怎么操作,以及开发工具怎么对接。适合正在被多项目版本冲突折磨、或者准备升级 Flutter 但担心回不去的朋友参考。文中所有命令我都在 Windows 11 上实测过,老系统 Windows 10 理论上也没区别,照着抄就行。
1. 为什么需要 fvm:先看看全局 SDK 模式坑在哪
1.1 全局 Flutter 的“版本锁死”问题
默认的 Flutter 安装方式,是把flutter/bin目录加进 PATH,所有项目共用同一个 SDK。个人开发者写 demo 完全没问题,但一旦你同时维护多个项目,几个很现实的坑就会冒出来:
- 项目 A 依赖的老插件没跟上新版本,编译直接挂;项目 B 又必须用新版才能修某个 bug,两边对 SDK 版本的要求是冲突的。
- 执行
flutter upgrade升级到新版本后,想回退就只能删 SDK 目录或者重新下载解压,还要改 PATH、重新跑 flutter doctor,过程机械且容易出错。 - 团队成员各自 Flutter 版本不一致,有人 build 得过,有人 build 不过,最后定位半天发现只是 SDK 版本不一样。
这里最痛的一点是:Flutter 团队本身不推荐长期混用版本,但在真实项目里,你根本没法保证所有分支都立刻升级。flutter upgrade一执行,整个项目可能就进入“半迁移”状态:老语法报警告、依赖包要求更高 SDK、Gradle 配置不兼容。一旦出现这种情况,你再想退回去,成本往往比升级还高。
1.2 fvm 是怎么把这件事解决的
fvm 的思路不复杂:它把不同版本的 Flutter SDK 下载到一个统一目录(默认是FVM_HOME或者用户目录下的 fvm 缓存目录),然后通过符号链接或路径切换的方式,让每个项目在运行 flutter 命令时指向自己锁定的版本。
提一个很多人拿 fvm 和 nvm(Node 版本管理器)类比的说法。nvm 切换的是当前 shell 的全局 node 版本,fvm 更强的一点是支持项目级锁定。你在项目根目录执行fvm use 3.16.9,它会生成版本配置文件,之后在这个目录里用fvm flutter,用的固定就是这个版本,不会波及全局。团队成员 clone 仓库后只要执行fvm install或fvm use,就能拉到同一个版本。这种“项目自描述”的思路,其实比单纯切全局版本更符合工程化习惯。
fvm 只管 Flutter 这一层,它不会绕开 Android Studio、Xcode 等原生工具的限制。比如 Windows 桌面端编译需要 Visual Studio 的 C++ 工具链,这是 VS 自己要去装的,fvm 不背锅。但正因为职责单一,它的核心命令不算多,每个都很稳,学起来成本很低。
2. Windows 下安装 fvm:三种方式与避坑要点
2.1 方式一:通过 Chocolatey 安装
如果你机器上已经装了 Chocolatey,安装 fvm 就一条命令:
choco install fvm -y要求是用管理员权限打开的 PowerShell 或 CMD。装完之后重新开一个终端,敲fvm --version能出来版本号就算成功。这种方式最省心,后续升级用choco upgrade fvm就行。
我个人的建议是:如果你本来就在用 choco 管理 Windows 软件,优先走这条路。因为 Chocolatey 会处理好 PATH 和权限的细节,不会出现“命令装好了却找不到”的尴尬。唯一要注意的是部分公司电脑的组策略会限制 choco,报错信息五花八门,遇到这种情况就换下面两种方式。
2.2 方式二:通过 pub global 激活
fvm 本身是 Dart 写的,所以也可以用 pub 全局激活的方式安装:
dart pub global activate fvm这里有个先决条件:本机得有一个能用的 Dart 环境。如果你已经装过任意版本的 Flutter,直接用 Flutter 自带的dart命令激活即可。激活成功后,fvm 会被放到%LOCALAPPDATA%\Pub\Cache\bin目录下,你需要确认这个目录在 PATH 里,否则终端找不到fvm命令。
提示:用
where fvm可以快速确认命令路径是否被系统识别。如果提示找不到,打开“系统属性 -> 环境变量”,把%LOCALAPPDATA%\Pub\Cache\bin加到用户 PATH 中,重新开终端即可。
2.3 环境变量与安装后的自检
不管用哪种方式装完,我强烈建议把 fvm 的缓存目录挪出 C 盘。一个 Flutter 稳定版 SDK 解压后大概 2GB 以上,多装几个版本 C 盘直接就爆了。我当时没注意,默认缓存攒了六个版本后,C 盘剩余空间一度只剩 3GB,整个电脑都开始卡。
推荐这样配环境变量:
FVM_HOME=D:\fvm # fvm 的版本缓存目录 PUB_CACHE=D:\fvm\pub-cache # pub 包全局缓存,避免重复下载依赖配置完记得把%FVM_HOME%\default\bin加到 PATH,这个目录是 fvm 之后放置全局默认版本符号链接的地方,不加进来的话fvm global切完版本,你在其他目录敲flutter还是老版本。
安装后的自检顺序:
fvm --version fvm help如果fvm help能列出 install、use、global、list 等子命令,说明基础环境已经通了。接下来就可以开始装多版本 Flutter。
3. 核心实操:安装多版本、切换与升级回退
3.1 先看有哪些版本:fvm releases 命令
fvm 安装前,我习惯先看一眼远端有哪些可用版本:
fvm releases这个命令会列出 Flutter 官方发布过的稳定版、Beta 版、Dev 版等。如果你对某个大版本的小版本号没概念,也可以用:
fvm releases stable只看稳定版列表。注意这个命令需要联网访问 Flutter 的 release 接口,首次执行可能稍慢,多等几秒就行,不要急着 Ctrl+C。
选版本有个小经验:不要一上来就选最新稳定版,多看两个指标——一是这个版本对应 Dart 版本是多少,二是它发布的时间。如果项目里用的某个依赖包只支持到某个 Dart 版本,那 Flutter 版本选太高反而会引入编译问题。
3.2 安装指定版本并锁定项目:fvm install 与 fvm use
确认好版本号之后,安装指定版本:
fvm install 3.16.9安装过程其实就是下载和解压对应版本的 Flutter SDK,时间取决于网络。装完可以用fvm list确认本地已有哪些版本。
接下来是最关键的一步:在项目根目录执行:
fvm use 3.16.9执行后,fvm 会在项目里做两件事:
- 生成版本配置文件,新版本叫
.fvmrc或fvm_config.json,老版本就是.fvmrc,记录当前项目锁定的版本号。 - 创建
.fvm目录,里面有一个flutter_sdk符号链接,指向真正的 SDK 缓存位置。
以后在这个项目目录里,凡是执行 Flutter 相关命令,都要带 fvm 前缀:
fvm flutter --version fvm flutter pub get fvm flutter run -d windows等于说项目里的所有命令都被“绑”到了指定版本上。如果同一个仓库被不同同事 clone,大家执行fvm use时看到的版本号是一致的,就不会再出现“我这里能跑你那里跑不了”的情况。
3.3 版本升级与回退的真实场景复现
升级应该是最多人关心的。假设项目当前锁定在 Flutter 3.13.9,你想试试 3.19.5,传统做法是直接全局升级,然后听天由命。用 fvm 可以这样做:
fvm install 3.19.5 fvm use 3.19.5 fvm flutter pub get fvm flutter run这几条命令执行完,项目就已经在 3.19.5 上跑了。如果跑起来发现有问题,比如某个第三方插件不兼容,或者 Impeller 渲染引擎导致一些自定义画笔效果变了,回退也非常直接:
# 回到项目根目录 fvm use 3.13.9 --force fvm flutter pub get fvm flutter run--force是干嘛的?当项目里已经有一个锁定的版本而你要求切换时,fvm 可能弹出确认提示,加上--force就是跳过确认,脚本化操作时更省心。
这里额外提醒一个坑:fvm 只管 SDK,不管 pub 包的缓存。你从 3.19.5 回退到 3.13.9 后,pubspec.lock可能已经被新版本的依赖解析逻辑改过了,有些包的解析结果在旧 Dart SDK 下不兼容。这时候不要慌,先git checkout pubspec.lock把它还原到项目锁定的版本,然后重新fvm flutter pub get。如果项目里没提交 lock 文件,也可以fvm flutter pub downgrade,让 pub 自动解析出一套兼容旧版本的依赖组合。
全局版本也可以用 fvm 管理。如果你希望平时在任意目录敲flutter都默认指向某个版本,就执行:
fvm global 3.16.9这个命令会把符号链接写进%FVM_HOME%\default,配合前面你配好的 PATH,就能实现全局版本灵活切换。需要清理旧版本时:
fvm remove 3.13.9执行前看一眼fvm list,确认这个版本没有被别的项目引用,再删不迟。磁盘占用不是小事,尤其你像我一样喜欢“先装再说”的,定期清理是必须的。
3.4 两个 Flutter 项目并行开发的实操建议
多版本管理不只是命令的事,日常习惯也很重要。我现在的标准做法就是:每个项目目录内只用fvm flutter和fvm dart,全局那个 flutter 命令几乎只在初始化新项目的时候用。这个习惯能避免很多“我明明 fvm use 了为什么还是老版本”的困惑。
同时开两个项目调试时,建议不同版本的进程分开跑,尽量别共用同一个调试端口。Flutter 的调试服务默认端口是随机分配的,正常不会冲突,但如果你手动指定过--debug-port,多个版本同时跑同一端口就会报地址占用。
另外,多版本环境下最怕的就是.dart_tool目录缓存错乱。切换 Flutter 版本之后,建议老老实实执行一次:
fvm flutter clean fvm flutter pub get不要嫌慢,这一步能把很多“玄学报错”扼杀在摇篮里。
4. 开发工具配置与常见问题排查
4.1 VS Code 接入 fvm:别再手动改 SDK 路径
VS Code 里如果直接在设置里指向某一个 Flutter SDK 路径,那多版本切换就又回到手动模式了。更优雅的方式是:在项目根目录的.vscode/settings.json里写:
{ "dart.flutterSdkPath": "${workspaceFolder}/.fvm/flutter_sdk" }这样打开项目时,VS Code 会自动使用当前项目 fvm 锁定的 SDK 版本。注意这里用的是${workspaceFolder}变量,不要写成绝对路径,否则换个人 clone 就失效了。
还有一种方式是 VS Code 的 Flutter 扩展本身会识别项目里的.fvm目录,自动提示“是否使用当前项目的 Flutter SDK”,我在新版扩展里遇到得比较多。但为了避免 IDE 抽风,我仍然会在项目级 settings.json 里显式写好,双保险。
提示:如果你用
dart.flutterSdkPath之后,VS Code 仍然提示找不到 Flutter SDK,先到项目根目录执行一次fvm use <版本号>,确保.fvm/flutter_sdk符号链接真实存在。新 clone 下来的仓库往往还没执行这一步,IDE 自然找不到。
4.2 Android Studio 与命令行环境变量
Android Studio 里的配置方式大同小异。打开 File -> Settings -> Languages & Frameworks -> Flutter,把 Flutter SDK path 指向项目的.fvm/flutter_sdk。保存后重启一下 Android Studio,让新的 SDK 路径生效。
命令行层面的建议是:PATH 里留一个 fvm 的全局面板就够了。你不需要把每个版本的 Flutter 路径都加进 PATH,那样反而乱。日常在任意目录打开终端,敲flutter --version,看到的是fvm global指定的版本;进入某个具体项目后,一律用fvm flutter,它读的是项目配置。这个组合最清晰,也不容易出错。
4.3 实战中常见的坑与排查方案
这部分是我实操下来最想写的内容。fvm 本身不是万能的,你在 Windows 上遇到的不少报错其实是被 fvm 切换版本后暴露出来的。
报错一:unable to find suitable Visual Studio toolchain
这个报错一般出现在想编译 Windows 桌面端时。fvm 安装的 Flutter 只是 SDK,不会替你装 Visual Studio。新版 Flutter 编译 Windows 目标要求系统里有 VS 的“使用 C++ 的桌面开发”工作负载,或者至少装了 Visual Studio Build Tools。
排查步骤:
- 打开 Visual Studio Installer,确认安装了“使用 C++ 的桌面开发”工作负载。
- 确认“Windows 10/11 SDK”组件存在,有时工作负载装得不完整,这个组件会被精简掉。
- 装完之后重开终端,运行
flutter doctor -v,看 Visual Studio 那一项是否显示已安装。
如果只是想编译 Windows 桌面版,装 Visual Studio Build Tools 也够用,不需要完整版的 VS。但这个工具链和 fvm 没有关系,属于 Windows 桌面开发的硬性前提,容易在版本切换后第一次跑 Windows 目标时突然冒出来。
报错二:you are applying Flutter's main Gradle plugin imperatively using the apply script method
这个报错在 Flutter 版本升级后很常见,尤其是老项目。新版 Flutter 推荐在 Gradle 里使用声明式插件 DSL,老式项目里往往还是用apply plugin的方式引入 Flutter Gradle 插件。切换新版本后,Flutter 的 Gradle 插件会直接拒绝老写法。
我踩过之后总结的解法分两步:
- 如果只是想回退到旧版本继续干活,直接
fvm use 旧版本就能搞定,报错一般随之消失。 - 如果想留在新版本,得动 Gradle 配置,把 settings.gradle 里的
pluginManagement和pluginsDSL 按官方迁移文档调整。
这里其实体现出 fvm 的一个隐性价值:它让你有足够的时间做渐进式迁移,而不是被新版 SDK 赶着走。
报错三:fvm 命令找不到或者 fvm global 不生效
多半是 PATH 没配全。用where fvm查看 fvm 所在路径,用where flutter看全局 flutter 所在路径。如果flutter指向的还是老的 SDK 目录,说明 PATH 里%FVM_HOME%\default\bin没有加进去,或者加的顺序不对。
报错四:符号链接创建失败
Windows 上执行fvm use时如果提示创建符号链接失败,通常是权限问题。解决方式是:启用“开发者模式”。位置在设置 -> 隐私和安全性 -> 开发者选项 -> 开发人员模式,打开后重启终端再试。如果不方便开开发者模式,就用管理员权限执行终端。
报错五:C 盘空间被 fvm 和 pub 缓存占满
前面提过,这是我最痛的教训。解决方案就是把FVM_HOME和PUB_CACHE这两个环境变量都指到非系统盘,并把旧的缓存目录手动清理掉。pub-cache 是个容易被忽略的大户,flutter 项目跑多了,这里轻松攒出几个 G 的依赖包。
4.4 版本切换后的连带问题速查表
| 问题 | 可能原因 | 快速处理 |
|---|---|---|
| flutter 命令版本没变化 | PATH 未指向 fvm default | 确认%FVM_HOME%\default\bin已加入 PATH |
| VS Code 找不到 Flutter SDK | 项目没生成符号链接 | 项目根目录执行fvm use <版本> |
| Windows 桌面编译失败 | 缺少 VS C++ 工具链 | 装 VS Build Tools 并确认 flutter doctor |
| pub 依赖解析异常 | pubspec.lock 被新版本改过 | 还原 lock 文件并重新 pub get |
| C 盘空间不足 | SDK 和 pub 缓存在系统盘 | 修改 FVM_HOME 和 PUB_CACHE 到其他盘 |
| Gradle 插件报错 | Flutter 版本升级引起不兼容 | 回退版本或迁移到 plugins DSL |
| fvm use 提示符号链接失败 | 权限受限或开发者模式未开 | 开启开发者模式或管理员终端执行 |
5. 一点进阶玩法:把 fvm 用出“云环境”的感觉
fvm 用熟之后,你会慢慢发现它不只是“切版本”的工具。因为版本配置文件是项目仓库的一部分,它可以被 CI/CD 直接读取。比如公司内部构建机只要装了 fvm,执行到项目目录时先fvm install再fvm flutter build,构建环境就和本地完全一致了。这能省掉不少“本地能构建,服务器构建失败”的扯皮。
另外,fvm 也是做 Flutter 版本适配测试的最好助手。比如你写了一个插件,想知道它在 Flutter 3.13、3.16、3.19 下分别能不能编译,不用一个个手动调整全局 SDK,直接建三个目录分别锁不同版本,跑一遍就知道了。我甚至见过有人用脚本循环fvm use+flutter test来批量回归,效率非常高。
6. 总结我的使用心得
最后分享一点我自己的习惯。一开始我总觉得 fvm 是给团队协作才用的,个人开发者用不上,试过之后才发现这个想法很片面。就算只有一个项目,你也会有“想升级到新特性,但怕踩坑”的时候。fvm 最打动我的地方,是让升级这件事变成“可逆操作”:先切新版本跑两轮自动化测试,有问题一条命令退回旧版本,心里稳得多。
我现在的标准流程,每个项目不管大小都在根目录维护一份 fvm 版本配置并纳入版本管理。同事新 clone 下来,看一眼配置,执行fvm install加fvm use就能复现环境。真别把小版本缓存全留着,每个稳定版两个 G,攒一年 C 盘直接爆,定期用fvm remove清掉不再用的版本,比清理什么 Windows 临时文件划算多了。
工具这个事,简单直接才撑得住长期用。fvm 在 Windows 上的价值,就是让你在 Flutter 多版本这个混乱问题上,找到一个足够简单、又足够可靠的支点。希望这篇实战记录能帮你少走一些弯路。