前言

想在鸿蒙(OpenHarmony / HarmonyOS NEXT)上跑 Flutter 项目,第一道门槛就是装 SDK。与官方 Flutter 不同,鸿蒙版 Flutter 不是 Google 发布的 SDK,而是社区基于上游 Flutter 仓库打补丁维护的分叉——上游即 github.com/flutter/flutter,撰写时上游最新版本为 3.47.8。它有自己的架构改造、自己的仓库、自己的版本命名、自己的安装姿势。

本文是「鸿蒙Flutter框架 0day适配实战」系列的开篇。考虑到背景知识对后续所有篇目都是地基,本篇开头先用一章把背景讲清楚:Flutter 的三层架构长什么样、为什么一个为 Android/iOS 设计的框架「可以」跑在鸿蒙上、社区适配到底做了多少事。背景之后进入本篇主线,回答三个消费侧问题:

  • 鸿蒙版 Flutter 该从哪个仓库装?tag 与分支两种形态分别怎么装?
  • 3.44.9+ohos-0.0.1-canary1、3.47.8-ohos-1.0.0 这类版本号怎么读?
  • 怎么装、怎么验证装对了、装不了的时候怎么判断原因?

文中所有版本号、报错信息均取自真实环境实操记录(macOS arm64 + fvm 4.3.1,2026 年 9-10 月),可直接对照复现。

版本口径(2026-10 更新):当前推荐 Flutter OH 适配版 3.47.8-ohos-1.0.0 + DevEco Studio 26.0.0 Release + OpenHarmony SDK/API 26。安装首选 fvm fork 别名直装 tag(fvm install <别名>/3.47.8-ohos-1.0.0,本篇第五章是完整实测记录);手动 bootstrap 作为兜底路径,见第 2 篇。环境搭建请按 OpenHarmony-flutter 环境搭建指导 操作。

如果你要的是「自己维护 fork、把上游同步过来并发布」,那是生产侧的内容,本系列第 5 篇起展开,本篇只讲到消费侧。

一、背景:为什么会有鸿蒙版 Flutter

1.1 Flutter 的三层架构:只有一层和操作系统有关

先看官方给 Flutter 的定义:Flutter 是一个可扩展的分层系统,上层组件各自依赖下层组件,且不能越权访问更底层。官方架构概览见 docs.flutter.dev 架构文档。这条「不能越级」的规定听起来像洁癖,其实是「能不能适配新平台」的关键:每一层的边界清清楚楚,换掉最底层,上面三层根本感觉不到。

在这里插入图片描述

上图左侧是官方分层模型(App → Framework → Engine → Embedder → 操作系统),右侧是每一层源码在哪、产物是什么、鸿蒙动没动——这张表就是后面所有工作量的坐标系:

层实现语言源码位置产物形态鸿蒙适配
框架层 FrameworkDartpackages/flutter*/lib随 App 一起编译,无独立二进制改平台分支(本系列后文 421 文件)
引擎层 EngineC++engine/src/flutter三档 har + gen_snapshot 二进制工件新增 shell/platform/ohos(826 文件)
嵌入层 Embedder平台语言各平台模板工程HAP 里的 entry 工程整层重写(ArkTS + C++)

注意一个反直觉的点:这张官方表格里没有鸿蒙那一格。Android 嵌入层用 Java + C++,iOS/macOS 用 Swift + ObjC,Windows/Linux 用 C++——鸿蒙是空的,社区分叉补的就是它。一句话记住这节:三层复用、一层重写,换平台只需要给嵌入层和引擎的平台目录补一份实现。

1.2 一帧画面的旅程:适配落在哪一步

Flutter 渲染一条 vsync 到像素上屏,走五步:

  1. build:调用 build() 返回 Widget 子树(框架会按需展开,所以 DevTools 里的树比你写的深);
  2. layout:深度优先遍历,约束自上而下传递、尺寸自下而上上报,布局复杂度 O(n);
  3. paint:生成绘制指令(LayerTree / DisplayList);
  4. composite:RenderView.compositeFrame() 创建画面,完成后交给 dart:ui 的 Window.render()——这是 Dart 世界和 C++ 世界的交接点;
  5. rasterize:引擎(Impeller / Skia)把场景栅格化成 GPU 指令、提交、送显。

在这里插入图片描述

上图下排标出了鸿蒙适配的落点:前四步是纯 Dart 逻辑,一行都不用改——这是 Flutter 跨平台的立身之本;从第⑤步栅格化往后全要重做:拿什么窗口画(XComponent)、用什么 GPU API(Vulkan)、字体从哪来(鸿蒙的 OH_Drawing)、事件从哪进(NAPI),这四件事在鸿蒙上都是新写的。

这里顺手记三条诊断经验,后文踩坑时会反复用到:

  • 红屏、溢出条纹、布局慢 → UI 线程(build 太重或每帧重建太多);
  • 掉帧、首帧慢 → 栅格线程(着色器与纹理准备);
  • 编译零报错但所有文字图标全没了 → 断点在栅格化之前,是字体管理器/字体端口的问题(这个坑本系列第 10 篇专门讲)。

顺便解释一个常见困惑:为什么 DevTools 里的 Widget 树比你写的代码层级深?看这段源码就明白了:

// packages/flutter/src/widgets/container.dart(简化)
Widget buildContainer(...) {
  Widget current = child;
  // 颜色不是画在 Container 上,而是拆成一个独立的 ColoredBox 插进树里
  if (color != null) {
    current = ColoredBox(color: color, child: current);
  }
  // 同理:Image 展开成 RawImage,Text 展开成 RichText
  return current;
}

「树更深」不是 bug,是组合式设计的必然结果。

1.3 为什么「可以」适配:稳定 ABI 与三个接缝

Flutter 又不是为鸿蒙设计的,凭什么能跑?答案在官方架构文档里的两句话:

  1. 「Flutter 的界面构建、布局、合成和绘制全都由 Flutter 自己完成,而不是转换为对应平台系统的原生组件」;
  2. 「Flutter 引擎本身是与平台无关的,它提供了一个稳定的 ABI(应用二进制接口),包含一个平台嵌入层,可以通过其方法设置并使用 Flutter」。

第一句是 Flutter 与 React Native 的分水岭:RN 是「我写 JS,你的系统控件来渲染」,每支持一个平台就得跟那个平台的控件体系较劲;Flutter 反过来——我自己画像素,我不需要你提供我依赖的控件,我只需要你给一块能画的地方。第二句的「稳定 ABI」是工程上的定心丸:引擎对外承诺的二进制接口不轻易变,嵌入层可以独立演进。

把这两句合起来,适配的可行性就落地成三个接缝——引擎和操作系统之间仅有的三处必须握手的接口:

接缝职责AndroidiOS鸿蒙
① 像素出口创建 Flutter 可以写入的表面FlutterView(视图/纹理模式)FlutterViewController + CALayerXComponent → OHNativeWindow → VKSurface
② 输入与生命周期触摸、生命周期、窗口变化、事件循环ActivityUIViewControllerUIAbility/FlutterAbility + NAPI 桥
③ 平台能力跨语言调用MethodChannel + dart:ffi(Kotlin HashMap / Swift Dictionary)同左同一套 channel;FFI 需要 Abi.ohosArm64

接缝③最有意思,因为它基本不需要为鸿蒙重写:MethodChannel 的数据由 Dart 序列化成标准格式再反序列化到平台侧,鸿蒙只需在 ArkTS 侧实现同一套接收端。但这里埋着一个大坑——FFI 这条路依赖 Dart 的 Abi 枚举认得目标平台,官方 Dart SDK 里没有 Abi.ohosArm,所以鸿蒙上 FFI 相关能力必须靠定制 Dart SDK(1.4 节的 L1 层)。

官方还给自定义嵌入层留了一扇门:文档明确「开发者可以创建自定义的嵌入层」,并给了 VNC 帧缓冲和树莓派两个例子。所以鸿蒙写一套嵌入层不是野路子,是官方设计意图之内的事。

1.4 适配到底做了多少事:自底向上七层清单

「为什么可以适配」讲完了,这一节回答「适配到底做了多少事」。社区的工作可以拆成自底向上的七层,层与层之间有依赖顺序——上面任何一层都要求下面已经就位:

在这里插入图片描述

上图是完整的七层工作量与验收判据图。压缩成一张表(文件数来自 3.44.9→3.47.4 同步批次实录):

层做了什么验收判据
L1 Dart SDK/运行时补 Abi.ohosArm/ohosArm64/ohosX64、Platform.isOhos,重建 patched_sdk 与 dart-sdkDart 里能打印 Abi.ohosArm64;flutter analyze 0 error
L2 引擎平台目录新增 shell/platform/ohos(C++):窗口、输入、生命周期、插件注册、字体端口三档引擎 ninja 0 FAILED;引擎单测真机 1723/1723
L3 渲染后端Impeller 走 Vulkan:建面、外接纹理、GPU 同步、防花屏真机有画面、不花屏、首帧时延达标
L4 宿主与桥接ArkTS 侧 FlutterAbility / XComponent + NAPI 桥flutter run 热重载可用;返回键与生命周期正确
L5 框架层TargetPlatform.ohos、默认字体与滚动/返回手势字体图标可见;交互符合鸿蒙习惯
L6 工具链flutter_tools/lib/src/ohos/ 13 个源文件 + cache.dart 工件接线flutter doctor -v 全绿;devices 能列出鸿蒙设备
L7 发布与分发版本锁 + 工件发布 + Release 附件全新机器装 tag → build hap → 零手工步骤

七层清单里有两个对消费侧很有用的信号:

  • L6 工具链:flutter doctor -v 能不能全绿、flutter devices 能不能列出鸿蒙设备,是你判断「SDK 装对了没有」的最快信号(本篇 5.4 节的自检会用到);
  • L7 发布与分发:L1/L2 的产物是二进制工件,有特别阴的特征——改了源码没重发、或重发了没换 tag,用户侧完全无感,静默用旧货。这也是后文版本核验要盯到 flutter.version.json 粒度的原因。

本篇只站在消费侧视角:怎么拿到、装上、验证这七层工作已经打包好的产物。
七层工作本身的生产侧细节(怎么改、怎么验证、怎么发布),本系列第 4-11 篇逐层展开。

二、鸿蒙版 Flutter:仓库与两种发布形态

2.1 它不是官方 SDK

先明确一个概念:鸿蒙版 Flutter(下文简称 ohos Flutter)是**分叉(fork)+ 补丁(patch)**的产物。社区团队拿到上游 Flutter 仓库的源码,在其上叠加一套面向 OpenHarmony 的适配补丁——就是 1.4 节那七层。

这意味着三件事:

  • 版本号里会带 -ohos 标记,与官方版本号体系平行但不对等;
  • 它的更新节奏跟随上游发版,但不是自动同步,每次同步都是一次完整的大工程(涉及数千文件重打与冲突裁决);
  • 安装来源是社区仓库,不是 flutter.dev 的官方发行版。

2.2 仓库与发布形态

需要关注的维护线只有一条:atomgit.com/oh-flutter/flutter_flutter。它最早 fork 自社区的另一支鸿蒙分叉,之后持续同步上游 github.com/flutter/flutter 并做鸿蒙适配,因此同步到的上游版本更新。两支的用法完全一致——同样的目录结构、同样的版本命名、同样的安装命令,本系列后续只以 oh-flutter 这条线为准。

真正决定你怎么安装的,是发布形态(同一个仓库里两种形态都有):

形态例子怎么查安装引用
git tag(当前发布线)3.47.8-ohos-1.0.0、3.47.4-ohos-1.0.4git ls-remote --tagsfvm install <别名>/<tag>
分支名(历史线)oh-3.44.9-dev、oh-3.35.7-releasegit ls-remote --headsfvm install <别名>/<分支>

仓库统一托管在 AtomGit,直接 git clone https://atomgit.com/oh-flutter/flutter_flutter.git 即可。

这里先把本篇最重要的结论摆出来,后面几章都是在证明它:tag 版本不需要绕开 fvm。fvm 的 fork 别名接受任意 git 引用(tag、分支、commit 都可以),把 tag 名当版本参数传进去就能装,装完还会自动跑首次初始化。第五章是这条路径的完整实测(clone 76.8 秒 + 自动下载定制 Dart SDK)。

2.3 与上游的关系:同步 + 适配

每个 ohos 版本都基于某个上游 Flutter 版本打出,它与上游的关系可以拆成两半:

  • 同步:框架代码、Dart SDK、DevTools 跟随上游演进,flutter.version.json 里的 frameworkRevision 是 fork 自己的提交;
  • 适配:fork 额外维护两套定制产物——
    • 定制引擎:bin/internal/engine.ohos.version 指向 ohos 引擎,与上游 engine.version 不是同一个值(这是故意的,不是 bug);
    • 定制 Dart SDK:bin/internal/dart-sdk-url.ohos 指定下载地址,因为官方 dart-sdk 不支持鸿蒙 ABI(1.3 节接缝③埋的坑)。

还有一个实用入口:仓库根目录的 CHANGELOG_OHOS.md 是 ohos 专属变更日志(不是上游的 CHANGELOG.md),升级前值得通读——ohos 侧修了什么(PlatformView 坐标、GPU 资源回收、输入法回调……)只有这里写得最权威。

三、版本命名规则:看懂 -ohos- 补丁号

3.1 分支名:oh-<上游版本>-release

例如 oh-3.44.9-dev,这是仓库里的分支名(历史线以分支发布,当前线以 tag 发布,见 2.2 节)。这里有一个最容易踩的认知坑:

分支名不等于实际版本号。
oh-3.44.9-dev 装出来,flutter --version 显示的是 3.44.9+ohos-0.0.1-canary1——分支 HEAD 已经比 3.44.9 多打了几轮补丁。

所以任何「以版本为准」的判断,都不要看分支名,要看装好之后的元数据(3.3 节)。

3.2 tag 名:<上游版本>-ohos-<补丁版本>

oh-flutter 仓库以 tag 发布,例如 3.47.5-ohos-1.0.0:

  • 3.47.5 = 对应上游 Flutter 3.47.5;
  • 1.0.0 = ohos 分叉自己的迭代补丁号,同一补丁线还有 1.0.1、1.0.2……

命名结构图解(含「分支名 ≠ 实际版本」对照):

在这里插入图片描述

上图展示了 3.47.5-ohos-1.0.0 的版本号解剖,以及分支名与实际版本的对照关系;右半部分是定制引擎的版本锁体系(生产侧内容,本篇只需知道「引擎版本与上游不同是正常的」)。

3.3 最终权威:flutter.version.json

每个已安装 SDK 的 bin/cache/flutter.version.json 里有最终生效的版本元数据,这是判断装没装对的唯一权威来源:

{
  "frameworkVersion": "3.47.5-ohos-1.0.0",
  "channel": "[user-branch]",
  "repositoryUrl": "https://atomgit.com/oh-flutter/flutter_flutter.git",
  "frameworkRevision": "39481acce8b9e6bb902714e6705c8baf75662d49",
  "dartSdkVersion": "3.13.4",
  "devToolsVersion": "2.60.0"
}

各字段含义与判断方法:

字段含义怎么用
frameworkVersion最终版本号(X.Y.Z-ohos-N.M.K)目标版本以此为准
repositoryUrl实际来源仓库一眼看出这套 SDK 是从哪个仓库、哪个域名装的(旧域名装的会留痕,见 CP3)
frameworkRevisionfork 框架提交判断是否为最新补丁
engineRevisionfork 使用的引擎与上游 engine 不同属正常
dartSdkVersionDart SDK 版本与上游大版本一一对应(3.44→3.13,3.47→3.13)
devToolsVersionDevTools 版本同步成功的旁证

四、安装前准备

4.1 前置条件

  1. 安装 fvm 4.x(本机为 4.3.1,Homebrew 安装,fvm --version 可验证)——fork 别名是这条安装路径的前提,fvm fork --help 能看到 add / list / remove 三个子命令;
  2. 了解 fvm 的 SDK 缓存目录:~/fvm/versions(fvm list 会显示 Cache directory);
  3. 预留磁盘与时间:分叉仓库体量大,且首次初始化拿到的 Dart SDK 是鸿蒙定制版——以 macOS(arm64)为例,dart-sdk-darwin-arm64-ohos.zip 是 225,700,875 B ≈ 215 MiB,Windows x64 对应包是 206.6 MiB,再加 HAP 构建专用运行时 dart-sdk-ohos.zip(8.5 MiB)与引擎 har / patched_sdk。实测两套体积:fvm 直装 tag 落地 3.1 GB(.git 占 2.0 GB,完整克隆),手动浅克隆 bootstrap 落地 1.4 GB(.git 只有 141 MB);时间都在两分钟内(5.2 节实测 1 分 49 秒)。全量工件表见第 9 篇。

fvm 在这里的角色是「多版本 SDK 管理器」:不同项目绑定不同版本的 Flutter(含 fork),互不污染。如果你不需要多版本管理,直接手动 clone 也可以,但 fvm 能让 flutter 命令自动解析到项目绑定的版本,体验更好。

4.2 国内镜像配置

国内网络下,务必先切镜像,否则引擎产物会从海外 OBS 慢速下载,第一步就可能卡死:

export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
export PUB_HOSTED_URL=https://pub.flutter-io.cn

提示:这两个环境变量建议写进 ~/.zshrc 或 ~/.bashrc 长期生效。
其中 PUB_HOSTED_URL 指向 pub.flutter-io.cn 国内镜像,后续 flutter pub get 也依赖它。

4.3 先查仓库里到底有哪些 tag / 分支

fvm 不会帮你列 fork 的引用(fvm releases 只给上游发布列表),装之前先用 git ls-remote 看仓库里有什么:

git ls-remote --tags https://atomgit.com/oh-flutter/flutter_flutter \
  | sed 's|.*refs/tags/||' | grep -v '\^{}' | sort -V | tail -11

实测输出(2026-10-28 记录,节选尾部):

3.41.10-ohos-1.0.0
3.41.10-ohos-1.0.1
3.47.4-ohos-1.0.0
3.47.4-ohos-1.0.1
3.47.4-ohos-1.0.2
3.47.4-ohos-1.0.3
3.47.4-ohos-1.0.4
3.47.5-ohos-1.0.0
3.47.7-ohos-1.0.0
3.47.7-ohos-1.0.1
3.47.8-ohos-1.0.0

历史分支线仍在同一个仓库里,需要对照时查 heads:

git ls-remote --heads https://atomgit.com/oh-flutter/flutter_flutter \
  | sed 's|.*refs/heads/||' | grep '^oh-' | tail -8
oh-3.35.7-release
oh-3.41.9-dev
oh-3.41.9-release
oh-3.44.9-dev
oh-3.47-dev
oh-3.47.4-dev

计数口径提醒:git ls-remote --tags 会把 peeled 引用(xxx^{})一起打出来,数 tag 之前先 grep -v '\^{}'。实测这个仓库的 --tags 输出 109 行、--heads 输出 55 行,去掉 peeled 后真正的 tag 是 83 个。

命名规律统一:<上游Flutter版本>-ohos-<分叉补丁号>,canary / beta 后缀表示预发布,生产环境选无后缀的正式版 tag。83 个 tag 里 60 个严格符合这条规律,剩下 23 个是历史形态,四类值得知道:

  • oh-3.47.4-rc1:候选期产物,唯一的 oh- 前缀 tag,只挂了 1 个定制 Dart SDK 附件;
  • 裸 3.47.4:不带 -ohos- 段,别当成 OH 发布版;
  • 3.44.9+ohos-0.0.1-canary1:用 + 而非 - 接 build metadata,正是 3.1 节那个「分支名 ≠ 版本号」的产物形态;
  • github.com/flutter/flutter/3.27.4 这类 3 个:同步锚点 tag,标记「同步到上游哪个提交」,不是发布版。

另外 tag 列表里没有 3.47.6-ohos-*——上游 3.47.6 → 3.47.7 全是 iOS 平台改动,与 OHOS 无关,所以 3.47.6 没有独立发布,改动直接并入 3.47.7-ohos-1.0.0 批次(这条判断在 bin/internal/dart-sdk-url.ohos 的注释里有原始记录)。

五、路径 A 实操:用 fvm fork 直装 tag 版本(推荐)

适用场景:tag 和分支都能用这条路径装,这也是本系列推荐的第一条路。两条安装路径的整体决策流程:

在这里插入图片描述

上图左绿是本篇的推荐路径(fvm fork 直装,引用写 tag 或分支都行),右蓝是兜底的手动 bootstrap(第 2 篇);中间橙色框就是本章要先破除的误区。装完统一做 5.4 节的三步自检。

5.1 注册 fork 别名

fvm fork add ohos https://atomgit.com/oh-flutter/flutter_flutter.git
fvm fork list        # 确认别名指向哪个仓库

ohos 是给这个仓库起的别名(alias),叫什么随意,它只是本机缓存目录的前缀 ~/fvm/versions/<别名>/;之后安装统一用 <别名>/<版本引用>,引用可以是 tag、分支,也可以是 commit。配套三个子命令:fvm fork add / fvm fork list / fvm fork remove。

小坑:fvm 只把「别名 → 仓库 URL」记在本地,不会帮你列这个仓库有哪些 tag 或分支,引用名得靠 4.3 节的 git ls-remote 自己查;而且别名指错仓库时注册不会报错,要等到安装那一步才失败——所以装之前先 fvm fork list 核对一遍。

5.2 安装 tag 版本(完整实测)

fvm install ohos/3.47.8-ohos-1.0.0

3.47.8-ohos-1.0.0 就是 4.3 节查到的 tag 名,直接当版本参数传给 fvm。真实输出(fvm 4.3.1,2026-10-28 记录;这台机器的别名起名叫 nutpi,换成你自己的别名即可,其余逐字一致):

Installing Flutter SDK: SDK Version : nutpi/3.47.8-ohos-1.0.0...Using forked repository URL: https://atomgit.com/oh-flutter/flutter_flutter.git
✓ Flutter SDK: SDK Version : nutpi/3.47.8-ohos-1.0.0 installed! (76.8s)
Setting up Flutter SDK: 3.47.8-ohos-1.0.0
Downloading Darwin arm64 Dart SDK from Flutter engine cf636b1dfb818dd6a3ff16e390e93b1a036969c8...
OHOS: using customized Dart SDK (dart-sdk-darwin-arm64.zip) from https://atomgit.com/oh-flutter/flutter_flutter/releases/download/3.47.8-ohos-1.0.0/dart-sdk-darwin-arm64-ohos.zip
OHOS: downloading customized Dart SDK runtime (dart-sdk-ohos.zip) from https://atomgit.com/oh-flutter/flutter_flutter/releases/download/3.47.8-ohos-1.0.0/dart-sdk-ohos.zip
Building flutter tool...
Resolving dependencies...
Got dependencies.
Flutter 3.47.8-ohos-1.0.0 • channel [user-branch] • https://atomgit.com/oh-flutter/flutter_flutter.git
Framework • revision 9d2002f780 (4 days ago) • 2026-10-25 07:12:07 +0800
Engine • hash dfd2e6cb7d319db05e3bf1503f3e2ac60f6288a2 (revision cf636b1dfb) (5 days ago) • 2026-10-23 02:13:38.000Z
Tools • Dart 3.13.4 • DevTools 2.60.0

四条值得注意的信息:

  1. clone 只花 76.8 秒。fvm install 默认带 --setup,装完接着跑首次初始化:下载定制 Dart SDK(215 MiB)与 HAP 构建运行时(8.5 MiB)、编译 flutter tool,端到端 1 分 49 秒。不想让它自动初始化就加 --no-setup;
  2. 日志里那两行 OHOS: ... from .../releases/download/3.47.8-ohos-1.0.0/... 就是第 9 篇讲的覆盖行机制在起作用——fork 把 Dart SDK 的下载地址改指到了自己的 Release 附件,官方源上没有这两个包;
  3. 落地目录是 ~/fvm/versions/<别名>/<tag名>(本机 ~/fvm/versions/nutpi/3.47.8-ohos-1.0.0),目录名就是 tag 本身,不需要自己起名;
  4. 这套副本的 HEAD 停在 tag 上(detached),.git 是完整克隆(实测 3.1 GB,其中 .git 占 2.0 GB)。别去 git pull,要升级就装新 tag;想省磁盘就走近 2 篇的浅克隆。

5.2b 分支形态同理(历史线对照)

同一套命令,把引用换成分支名即可:

fvm install ohos/oh-3.35.7-release

装完 flutter --version 报出来的是 3.35.8-ohos-1.0.4——分支名 ≠ 实际版本号(3.1 节),一切以 flutter.version.json 为准。同理 oh-3.44.9-dev 装出来是 3.44.9+ohos-0.0.1-canary1。

5.3 在项目里切换

cd <你的项目目录>
fvm use ohos/3.47.8-ohos-1.0.0

这会在项目根目录生成/更新 .fvmrc,并创建 .fvm/ 软链:

{
  "flutter": "ohos/3.47.8-ohos-1.0.0"
}

两个配套动作:

  • 把 .fvm/ 加进项目的 .gitignore(软链指向本机绝对路径,提交进仓库会坑到队友);
  • 项目内所有 flutter 命令此后都走 fvm 解析:fvm flutter pub get、fvm flutter run -d <设备>。

5.4 装完三步自检

装完不要直接开跑,按顺序做三层核验(层层递进,越往后越权威):

  1. fvm 层面:确认缓存目录结构与项目绑定关系;
  2. flutter 层面:确认最终生效版本符合预期;
  3. 元数据层面:确认 flutter.version.json 的每个字段都对得上。
# ① fvm 层面
fvm list

# ② flutter 层面
fvm flutter --version

# ③ 元数据层面(最权威)
cat ~/fvm/versions/<别名>/<版本名>/bin/cache/flutter.version.json

第三层看到的 frameworkVersion / repositoryUrl / dartSdkVersion 与 3.3 节表格对照,全部符合预期,才算「装对了」。如果涉及真机运行,再加一层工具链自检(对应 1.4 节 L6 的验收判据):

fvm flutter doctor -v     # 鸿蒙工具链是否就绪
fvm flutter devices       # 能否列出鸿蒙设备/模拟器

六、什么时候仍需手动 bootstrap

fvm 直装 tag 已经跑通,路径 B 就不是「tag 的唯一解」了。它剩下的价值是可控与省磁盘,但在讲场景之前,得先把「fvm 装 tag 会失败」这个流传很广的说法纠正过来。

6.1 先看清失败的真实原因:不是不认 tag,是去错了仓库

最常见的失败长这样:

fvm install 3.47.8-ohos-1.0.0        # ← 少了别名前缀

加 --verbose 跑一次,日志会直接交代它在干什么(真实记录):

Skipping git reference validation for version: 3.47.8-ohos-1.0.0
Git cache not found. Creating heads/tags cache...
Creating local git cache...

也就是说:fvm 把这个字符串当成一个git 引用去查,而它用的默认仓库是 https://github.com/flutter/flutter.git——建本地 bare 缓存时写进 config 的 remote url 就是这条。上游仓库里当然没有 3.47.8-ohos-1.0.0,于是卡住或失败。结论:fvm 认 tag,不认的是「没有 fork 别名时的默认仓库」。

成因判据处置
忘了写别名命令里没有 <别名>/ 前缀fvm install <别名>/<tag>(5.2 节)
别名指向不对fvm fork list 打出的 URL 不是你要的仓库fvm fork remove <别名> 再重新 add
fvm 太旧没有 fork 能力fvm fork --help 报错升到 4.x(本机 4.3.1 实测可用)

6.2 仍然该走手动 bootstrap 的四个场景

  1. 机器上不想装 fvm:CI 镜像、临时排障机、给别人复现问题;
  2. 要省磁盘:浅克隆只留 1 个提交,实测落地 1.4 GB,对比 fvm 全量克隆的 3.1 GB(4.1 节);
  3. 要精确控制落地位置与目录名:例如把 SDK 固定放到约定路径给脚本用;
  4. 要看清 fvm 替你做了什么:首次初始化、flutter_tools 的 SSH 依赖 URL、镜像变量这几个坑,手动走一遍才知道边界在哪——这正是第 2 篇存在的全部理由。

口径:遇到「版本墙」(第 8 章详述),解法永远是升分叉版本;升到哪个版本看 4.3 节的 tag 墙,装法优先 fvm 直装,上面四种场景才走手动。

七、版本对照表:已核实的五套 SDK

7.1 本机实装 SDK 一览

以下元数据全部取自本机实际安装 SDK 的 bin/cache/flutter.version.json(2026-10-28 记录):

ls ~/fvm/versions/*/
# nutpi/: 3.47.7-ohos-1.0.1  3.47.8-ohos-1.0.0
# ohos/:  oh-3.32.0-release  oh-3.35.7-release  oh-3.47.5-release
fvm 目录(别名/版本)frameworkVersion来源仓库frameworkRevision提交日期engineRevisionDartDevTools装法
nutpi/3.47.8-ohos-1.0.03.47.8-ohos-1.0.0atomgit/oh-flutter9d2002f7802026-10-25cf636b1dfb3.13.42.60.0fvm 装 tag
nutpi/3.47.7-ohos-1.0.13.47.7-ohos-1.0.1atomgit/oh-flutter62d280c5bc2026-10-21deb287481e3.13.42.60.0fvm 装 tag
ohos/oh-3.47.5-release3.47.5-ohos-1.0.0gitcode/oh-flutter39481acce82026-09-23af7e796e163.13.42.60.0手动浅克隆
ohos/oh-3.35.7-release3.35.8-ohos-1.0.4atomgit/CPF-Flutter04d2008b352026-09-24035316565a3.9.22.48.0fvm 装分支
ohos/oh-3.32.0-release3.32.4-ohos-0.0.1atomgit/CPF-Flutterdae30486e62026-05-218cd19e509d3.8.12.45.1fvm 装分支

这张表里有五条可以直接抄走的判据:

  1. 目录名会出卖你的装法:fvm 装 tag 时目录名就是 tag 本身(nutpi/3.47.8-ohos-1.0.0),fvm 装分支时目录名是分支名,手动 bootstrap 时目录名是你自己起的(第三行 oh-3.47.5-release);
  2. 分支名 ≠ 版本号:oh-3.35.7-release 实际是 3.35.8-ohos-1.0.4,oh-3.47.5-release 实际是 3.47.5-ohos-1.0.0——一切以 frameworkVersion 为准(3.1 节);
  3. 旧域名会在元数据里留痕:第三行的 repositoryUrl 是 atomgit.com/oh-flutter/...,那是仓库迁移前的地址。现在统一用 AtomGit(CP3),但已装好的 SDK 不会自动改写这个字段,看它就能判断这套 SDK 是什么时候、从哪个域名装的;
  4. Dart 版本能反推上游线:3.32→Dart 3.8.1、3.35→3.9.2、3.47→3.13.4,跨线升级必然伴随 Dart 大版本变化,依赖要重解(8.3 节);
  5. 多套 SDK 共存互不干扰:同一台机器上五种装法、三个来源都在,靠各项目 .fvmrc 里那一行决定用哪个。

最后两行是更早的分支历史线(3.32 / 3.35),保留在这里只作版本对照——用法与 oh-flutter 完全一致,本系列后续一律以 oh-flutter 的 tag 线为准。

7.2 上游 ↔ 分叉版本换算规则

  1. 已知上游官方大版本 X.Y:分叉分支名形如 oh-X.Y.Z-release,分叉 tag 形如 X.Y.Z-ohos-N.M.K;
  2. 想确认某个分叉基于哪个上游:看 dartSdkVersion——Dart 版本与上游 Flutter 版本一一对应(3.44→3.13、3.47→3.13);
  3. 引擎版本不要与上游对比:engineRevision 是 ohos 定制引擎,与上游 flutter/engine 的 revision 没有可比性,对不上是正常现象。

上游参照(本机实测的官方对应关系):

上游 FlutterDartDevTools
3.47.x3.13.42.60.0

7.3 版本选择建议(基于历史结论)

需求选哪个理由
稳定开发、生态验证最充分oh-flutter 3.47.8-ohos-1.0.0Dart 3.13.4,当前发布线,实测 pub 可解析
需要旧线行为对照oh-3.35.7-release(实际 3.35.8-ohos-1.0.4)分支形态的历史线,同一套 fvm 命令
回退到上一版3.47.7-ohos-1.0.13.47 线上一发布,fvm 装法完全相同

选型的核心原则只有一条:先看第三方依赖的最低 Flutter 约束,再在满足约束的分叉版本里挑最新补丁号。约束不满足时,任何配置技巧都绕不过去——只能升版本。

八、版本墙:第三方包逼你升级

8.1 真实报错

版本墙是消费侧最常见的「硬阻断」。真实记录如下(flutter pub get 失败):

The current Flutter SDK version is 3.44.9+ohos-0.0.1-canary1.
Because <package> depends on <dep> which requires SDK version >= 3.41.0,
version solving failed.
...suggestion to make the pubspec resolve:
* Try using the Flutter SDK version: 3.47.6.

8.2 读懂报错:别照着建议版本号装

这个报错里有一个极具误导性的细节:

建议的 3.47.6 是上游官方版本号,不是 ohos 分叉的版本号——本机根本装不了这个版本。
正确动作是把 ohos SDK 升到满足约束的分叉版本(本例为 3.47.8-ohos-1.0.0,框架功能对应上游 3.47.x)。

处理路径按顺序执行:

  1. 确认目标分叉版本存在(4.3 节查分支/tag);
  2. 装好新 SDK:fvm install <别名>/<tag>(5.2 节);只有在 6.2 节那四种场景下才走手动 bootstrap;
  3. 项目内 fvm use 切换到新版本;
  4. 重新 fvm flutter pub get,确认解析通过。
# 升级后一键核验:版本横幅 + 依赖解析
fvm flutter --version && fvm flutter pub get

8.3 大版本升级后的三个注意点

3.44 → 3.47 跨了一个大版本,Dart 从 3.13.x 跟进到 3.13.4,升级不是无感的:

  • 依赖要重解:切换后必须重新 flutter pub get,旧的 .dart_tool 解析结果作废;第三方包/插件可能需要重新适配,或自身存在 API 变更;
  • 引擎不要对比上游:引擎是 ohos 定制引擎(engine.ohos.version),拿上游 engine.version 去对是错误姿势,两者故意不同;
  • 升级前先读 CHANGELOG_OHOS.md:它列出了 ohos 侧每个补丁修了什么。下面是一段真实摘录(3.44.9+ohos-1.0.5,2026-09-24):
### Added
- feat(flex_overflow): dynamic DPI overflow strategy,修复动态DPI下PlatformView点击事件坐标分布异常
- feat: parseOhosVersion supports +ohos build metadata format
- feat(ohos): 完善GPU资源回收全流程日志埋点

### Changed
- change(Template): OHOS模板targetSdkVersion升级至26.0.0并统一compatibleSdkVersion至5.0.5(17)(自旧版本线延续)

### Fixed
- 修复StandardMessageCodec.writeValueInternal绕过子类覆写导致序列化失败(自旧版本线延续)
- fix(safearea): correct safe area padding for edgeToEdge and freeform window exit
- fix(DEPS_ohos): update skia_revision for font alias fix

升级决策完全可以基于它做:需要哪个修复/能力 → 找到含该条目的版本 → 用对应的 tag/分支装。

九、常见问题 FAQ

9.1 高频问题速查

把本篇涉及的所有坑汇总成一张速查表(后文篇目会展开更细的坑体系):

#现象原因解法
CP4fvm install 卡死在下载未切国内镜像,走海外 OBS 慢速通道先 export 两个镜像变量(4.2 节)
CP9装了 oh-3.44.9-dev 却显示 3.44.9+ohos-0.0.1-canary1分支 HEAD 已含后续补丁正常现象,以 flutter.version.json 为准(3.1 节)
CP5fvm install 3.47.8-ohos-1.0.0 卡住/失败没带 fork 别名,fvm 拿默认仓库(官方 flutter/flutter)去查这个引用写成 fvm install <别名>/<tag>,先 fvm fork list 核对指向(6.1 节)
CP11队友 clone 项目后 flutter 命令失效.fvm/ 软链被提交进仓库,指向你的绝对路径.fvm/ 加入 .gitignore,队友自行 fvm use(5.3 节)
CP8pub get 报 requires SDK version >= 3.41.0版本墙:分叉版本低于依赖约束升分叉版本,别照报错里的上游版本号装(第八章)
CP12用上游 engine.version 对比 ohos 引擎「对不上」ohos 定制引擎与上游故意不同看 engine.ohos.version,不与上游对比(7.2 节)
CP3旧文章/脚本里的仓库域名与实际不符仓库已统一迁移到 AtomGit一律改用 atomgit.com 地址(2.2 节)

9.2 现在应该装哪个版本?

直接装 oh-flutter 的 tag 3.47.8-ohos-1.0.0,一条命令:fvm install <别名>/3.47.8-ohos-1.0.0(5.2 节实测)。只有维护旧项目、需要复现历史分支线行为时,才把引用换成分支名(5.2b 节)。DevEco Studio 配套 26.0.0 Release,SDK/API 为 26。

9.3 fvm install <别名>/<tag> 还是失败,怎么排?

按 6.1 节的三条成因过一遍:别名前缀有没有写、fvm fork list 里别名指向的仓库对不对、fvm 是不是旧到没有 fvm fork 子命令。三条都排掉了,再加 --verbose 看它到底在跟哪个仓库要哪个引用;确认是环境限制(磁盘、CI、离线)才转手动 bootstrap(第 2 篇)。

9.4 环境变量该按哪个版本配置?

以 DevEco Studio 26.0.0 Release 的安装路径为准:DEVECO_SDK_HOME 指向 .../Contents/sdk,JAVA_HOME 指向 .../Contents/jbr/Contents/Home,并把 ohpm、node、hvigor、hdc 加入 PATH;完整步骤见文首环境搭建指导。

十、系列导航

本文是「鸿蒙Flutter框架 0day适配实战」系列的第 1 篇。全系列 10 篇,分消费侧(4 篇)与生产侧(6 篇)两部分,背景内容已并入本篇开头,不再单独成篇:

#篇目侧一句话主题
01背景、分叉仓库、版本命名与 fvm fork 安装(本篇)消费三层架构、三个接缝、七层工作量、fvm 直装 tag、版本墙
02tag 版本的手动安装与三层核验消费浅克隆 bootstrap、首次初始化、SSH 修复、异常分诊
03鸿蒙模拟器/真机跑 Flutter + 消费侧踩坑集消费工具链三件套、HAP 构建/安装/运行、占位签名、截图取证、CP1-CP14
04flutter build hap 自动签名消费P12/CSR、AGC 证书与 Profile、纯 Dart AES-GCM、C0-C10 复用
05上游同步总览、BC 全景与端到端 SOP生产批次解剖、五阶段验证门、12 项适配表、端到端 SOP 与 P1-P29 索引
06Phase 1+2:基线建立与 Framework 适配生产代理 HTTP/1.1 修障、421 文件重打、Dart SDK 源码构建
07Phase 3 Engine:三方重放与冲突裁决生产826 文件、15 真冲突两分法、契约对齐 16 项
08引擎构建链攻防生产依赖树、工具链版本、链接与测试面 27 坑
09构建发布与产物分发生产版本锁、11 个工件(Dart SDK 双宿主)、11 条覆盖键、换新 tag 铁律
10验证与真机验收生产三档引擎、设备侧单测、字体图标静默故障

配套仓库与文档:

总结

本篇作为系列开篇,先用一章把背景讲透:Flutter 三层架构里只有嵌入层和操作系统直接相关,官方稳定 ABI 把平台差异压缩进「三个接缝」,社区适配做了 L1-L7 七层工作、每层都有明确验收判据;之后进入消费侧主线——一条 oh-flutter 维护线、两种发布形态(tag 与历史分支),分支名 ≠ 实际版本号,一切以 flutter.version.json 为最终权威;安装首选 fvm fork 别名 + tag,一条命令 1 分 49 秒装完并自动初始化,手动 bootstrap 退居兜底;遇到版本墙时,先读懂报错里「上游版本号」的误导,再选对分叉版本升级。

下一篇(第 2 篇)讲兜底路径:在没有 fvm、要省磁盘、或想看清 fvm 替你干了什么的时候,手动 bootstrap 一个 tag 版本(实例 3.47.5-ohos-1.0.0,当前最新为 3.47.8-ohos-1.0.0)的完整五步、三层核验与异常分诊。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

环境与版本

本文的示例与截图基于以下环境组合(2026-10 实测):

组件版本说明
上游 Flutter SDK3.47.8github.com/flutter/flutter
Flutter SDK(OH 适配版)3.47.8-ohos-1.0.0oh-flutter/flutter_flutter,oh-3.47-dev 分支
DevEco Studio26.0.0 Release提供 hvigor / ohpm / hdc / JBR 等工具链
OpenHarmony SDK / APIAPI 26(26.0.0)模拟器与 SDK 均为 API 26
运行设备OpenHarmony 模拟器API 26,1280×2832

环境搭建(分叉获取、DevEco Studio 与 SDK 配置、模拟器创建)请按官方指导操作:OpenHarmony-flutter 环境搭建指导。

本篇的安装命令全部在 fvm 4.3.1(Homebrew,macOS arm64)上实测;5.2b 的分支示例取 oh-3.35.7-release(实际版本 3.35.8-ohos-1.0.4),只为讲清「分支名 ≠ 实际版本号」。日常安装直接用 fvm install <别名>/3.47.8-ohos-1.0.0(5.2 节实测端到端 1 分 49 秒);第 2 篇的手动 bootstrap 是兜底路径,不是 tag 的唯一装法。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐