“在 MacBook M1 上把 Flutter for OpenHarmony 真正跑起来”——这事我前前后后折腾了小一周。一开始我并不觉得它有多难,毕竟 Flutter 的脚手架一直很省心,但 OpenHarmony 的生态和工具链跟 Android 完全不是一回事,M1 的 ARM 架构又反复给我挖坑。这篇文章就是我基于这次实战的环境搭建与基础入门记录,把每一步的选择原因、实际命令、报错排查都写清楚,希望可以帮到准备在 ARM Mac 上做 Flutter for OpenHarmony 开发的人,尤其是刚接触这套技术栈、想先“跑通一个 Hello World”的新手。
我不会只写“能跑通就行”的表面步骤,还会解释为什么必须用某个分支、为什么 JDK 要用 ARM 版、为什么签名配置总出问题。这样你遇到类似报错时,可以顺着思路自己排查,而不是复制粘贴命令后继续靠玄学。
1. 为什么要在 M1 Mac 上折腾 Flutter for OpenHarmony
1.1 这套组合到底解决了什么问题
先说结论:Flutter for OpenHarmony 是一个专门适配 OpenHarmony 的 Flutter 分支,它让 Flutter 编译出的工程可以产出 OpenHarmony 的安装包(hap),而不是只能产出 Android 的 apk 或 iOS 的 ipa。换句话说,你的 Dart 代码和 Flutter UI 可以基本不重写,直接跑在 OpenHarmony 设备上。
对团队或个人来说,这个价值非常直接:如果你们原本就是 Flutter 技术栈,要接 OpenHarmony 设备的适配需求,不必让所有人转学 ArkTS/ArkUI,而是继续用已经熟练的 Flutter 写法开发 UI,只在必要的时候通过平台通道去调 OpenHarmony 原生能力。Flutter 的自绘渲染引擎保证了同一套 UI 在不同平台上的表现基本一致,那么 Android/iOS/OpenHarmony 三端共用一个代码库就变成了现实。
不过要特别明确一点:这里说的“Flutter for OpenHarmony”不是 Google 官方 Flutter SDK 直接支持 OpenHarmony,而是 OpenHarmony 社区生态里维护的适配分支。它的核心工作是把 Flutter 引擎编译到 OpenHarmony 平台上,让 Dart 代码能跑起来,同时把 Flutter 的渲染接入 OpenHarmony 的图形能力。所以很多命令和官方 Flutter 很像,但 SDK 本身不能互替。
1.2 什么人适合读这篇
如果你是下面几类人,这篇文章应该能直接省下你的摸索时间:
- 手里只有一台 MacBook M1,想开发 OpenHarmony 应用,但又不确定工具链能不能装起来;
- 团队是 Flutter 栈,想评估“要不要接入 OpenHarmony”,需要先有一个人把环境跑通,出一份实测结论;
- 已经在 OpenHarmony 上用 ArkTS 写了一些页面,但觉得 Flutter 的动画、组件生态更顺手,想两套方案做对比;
- 纯粹想在自己的 ARM Mac 上开开眼界,看看这套“跨端组合拳”实际到了什么成熟度。
提前说明,这篇更偏“从零到跑通”的实战侧,不会深入讲 Flutter 引擎在 OpenHarmony 内部是如何被 native 侧渲染的。如果你想深入引擎层,建议读 fork 分支的源码和文档;我这边只讲应用开发视角。
2. M1 Mac 的基础工具链:先装对的,再装 Flutter
很多人的第一步是把 Flutter 装上,回头发现 OpenHarmony SDK 又装了一遍,Java 版本还不对,最后环境变量乱成一团。我的建议是:先把 OpenHarmony 应用开发需要的基础工具链理清楚,再拉 Flutter 分支。
2.1 JDK 选型:ARM 版是硬性要求,不能省
OpenHarmony 的构建工具链(打包、编译 hap 的工具)是跑在 JVM 上的,所以 JDK 是刚需。在 M1 Mac 上,这里的第一个坑就是:安装一个 x86_64 的 JDK 可能也能跑,但后续在构建 OpenHarmony 工程时容易出现诡异问题。
为什么?M1 是 ARM64 架构,x86_64 的 JDK 需要靠 Rosetta 2 翻译执行,翻译层对大多数 Java 程序没问题,但构建工具经常涉及大量 io、进程生成、文件映射,翻译后性能下降明显,而且偶发崩溃率会高。你无法确定某个奇怪报错到底是自己配置问题还是翻译层问题,所以最稳的方案就是直接用 ARM64 原生 JDK。
安装命令很简单,随便找一个提供 ARM64 安装包的 OpenJDK 发行版即可,我用的是 macOS 上的 Homebrew:
brew install openjdk@17 sudo ln -sfn /usr/local/opt/openjdk@17/libexec/openjdk17.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk export JAVA_HOME=$(/usr/libexec/java_home -v 17) java -version注意事项:OpenHarmony 工具链对不同 JDK 版本的要求会变,有的版本要求 JDK 11,有的要求 17,建议看官方 IDE 或工具链文档的兼容说明。我实测下来,JDK 17 在较新的 OpenHarmony SDK 上支持比较好,但如果你用的是旧版工具链,可能要降到 11。
装好后顺手验证一下是不是 arm64 的原生 JDK,用这条命令:
file /Library/Java/JavaVirtualMachines/*/Contents/Home/bin/java输出里有arm64字样就没问题。如果显示x86_64,说明装的是 Intel 版,请换一个 ARM 发行版重装。
2.2 官方 IDE、SDK 和命令行工具的定位
OpenHarmony 应用开发一般会用到一套集成开发环境,我会叫它“官方 IDE”。它不仅是编辑器,更承担了 SDK 管理、签名配置、模拟器镜像下载等角色。首次启动时它会引导你安装 OpenHarmony SDK 和工具链,这一步建议不要跳过,因为后面 Flutter 分支需要指向这个 SDK。
我理解有人习惯命令行优先,但 OpenHarmony 生态还没有完全命令行化。比如 iOS 有xcode-select式的统一管理,OpenHarmony 这边更常见的做法是 IDE 下载 SDK,命令行调用 IDE 附带的工具。所以我的路线是:先用 IDE 把 SDK 装好并跑通一个 ArkTS 工程,再回到终端折腾 Flutter。这个前置步骤能帮你确定“原生环境本身是好的”,之后 Flutter 出问题时,排查范围就大大缩小。
工具链里你会反复遇到三个命令:
hdc:设备连接和部署工具,类似 Android 的 adb,负责连接模拟器或真机,安装 hap,抓取日志;ohpm:OpenHarmony 的包管理器,类似 npm,负责拉取 ArkTS 依赖;hvigor:构建工具,类似 gradle,负责编译和打包 OpenHarmony 工程。
这些工具一般在 SDK 的toolchains目录下,建议把它们加进PATH:
export OHOS_SDK_HOME=/path/to/ohos-sdk export DEVECO_SDK_HOME=$OHOS_SDK_HOME export PATH="$OHOS_SDK_HOME/toolchains:$PATH" export PATH="/path/to/ohpm/bin:$PATH"不同版本的 SDK 环境变量名可能有差异,如果flutter doctor识别不到,先回 IDE 里看 SDK 的实际路径,再调整上面的变量名。不要死记一个名字。
3. 拉取 Flutter 的 OpenHarmony 分支:版本与架构辨析
3.1 为什么不能用官方 Flutter SDK
这一步是新手最容易卡住的地方:flutter create之后发现不支持 OpenHarmony,或者flutter build根本没有 hap 这个目标产物。原因很简单,官方 Flutter SDK 只认 Android/iOS/web/desktop 这些平台,它对 OpenHarmony 毫无感知,所以你在官方 SDK 里怎么配置都得不到 hap。
正确做法是拉取 Flutter for OpenHarmony 的 fork 仓库,它包含了对 OpenHarmony 平台的支持代码包、模板文件和构建脚本。拉取时我强烈建议按发布标签(release tag)而不是 master 分支,因为 fork 版本的迭代节奏不稳定,master 可能刚改完一半,直接上手容易被半成品状态坑到。
我的做法是:
git clone -b ohos <flutter_flutter 的官方 fork 仓库地址> flutter_flutter_ohos cd flutter_flutter_ohos git checkout <release 标签> export PATH="$PWD/bin:$PATH" flutter --version我这里刻意不写死仓库地址,因为这类 fork 仓库会随官方迁移和改名。最可靠的方式是去 OpenHarmony 官方位置的 SIG 领域索引里找“Flutter”仓库,认准官方的发布页,而不是搜索引擎里那些不明来源的第三方副本。很多第三方包版本滞后或者夹带私有修改,遇到问题你没法判断是上游问题还是它改坏了。
3.2 用 flutter doctor 验证 SDK 识别是否正确
拉完 Flutter 分支后,第一件事不是创建项目,而是先验证环境变量是否被 Flutter 认可。执行:
flutter doctor -v如果一切正常,你应该能看到 OpenHarmony SDK 被识别到,并且 toolchain 状态是绿色的。如果这里显示Cannot find OpenHarmony SDK,八成是上一节提到的环境变量没有设置对,或 IDE 安装的 SDK 路径和你指向的路径不一致。
打开一个终端,重新检查:
echo $OHOS_SDK_HOME ls $OHOS_SDK_HOME/toolchains确认路径真实存在后,再用flutter config指定 SDK。不同版本的命令略有出入,可以先看帮助:
flutter config -h一般会有一个类似flutter config --ohos-sdk=/path/to/ohos-sdk的选项。设置完成后再次运行flutter doctor -v,直到识别正常。
这里还有一个容易忽略的细节:首次构建 Flutter 引擎需要拉取预编译产物,建议提前把缓存准备好:
flutter precache --ohos如果你跳过了这一条,后面第一次构建时它才会临时去下载,耗时很长,而且 M1 还会遇到架构不匹配的麻烦。先跑这一句,能显著降低首跑的挫败感。
4. 创建第一个项目:从模板到工程结构
4.1 flutter create 生成的项目骨架
环境通过验证之后,创建项目反而很简单,关键是理解生成出来的结构。执行:
flutter create --platforms ohos my_first_ohos cd my_first_ohos如果 fork 分支支持--platforms ohos,它就会生成一个 OpenHarmony 壳工程。你会看到项目里有一个ohos目录和 Dart 标准的lib/main.dart。ohos目录内部的结构和 ArkTS 工程很类似:entry目录相当于可安装模块,里面有src/main/ets(壳入口)、src/main/resources(资源)、src/main/module.json5(模块配置)、build-profile.json5(构建配置)、oh-package.json5(依赖声明)。
你可以这样理解:entry相当于你包的“外皮”,负责在 OpenHarmony 系统里拉起一个 Ability,然后加载 Flutter 引擎和 Dart 代码;而你的 UI 逻辑、业务代码都还在lib/main.dart里。也就是说,大多数情况下你不需要理解 ArkTS 壳的那一坨代码,日常开发还是 Flutter 那套。
打开lib/main.dart,改一下中间的文字,改成类似Hello OpenHarmony from Flutter,保存。
4.2 核心配置文件该改什么
新手进入这个阶段通常会问一个问题:“我要不要手动改 ohos 里的配置?”答案是:前期不用改太多,但你要知道它们对应什么。
entry/src/main/module.json5里有应用的基本信息,其中deviceTypes指定支持的设备类型,abilities定义入口页面。你看到的应用名称、图标,一般在resources/base/element/string.json和resources/base/media里改。想改包名时,找到bundleName字段,它类似 Android 的 applicationId,不同应用之间必须唯一。
oh-package.json5是 ArkTS 侧的依赖声明。Flutter 模板默认会引入 Flutter 侧需要的依赖,正常情况不需要手动改。如果构建报依赖缺失,第一反应应该是去官方模板的对应版本对比差异,而不是自己去网上抄个新版本号填进去,版本错配是这类问题的主要来源。
我用一句话总结这部分:Flutter 代码是主体,OpenHarmony 壳工程是基础设施。先跑起来再谈定制,避免一上来改配置把简单问题复杂化。
5. 编译、签名、安装:让 App 跑到设备上
5.1 hap 构建与输出物
OpenHarmony 应用的可安装产物是 hap,它相当于 Android 的 apk。在 Flutter for OpenHarmony 分支里,构建命令一般还是走 Flutter 的 CLI,只是平台参数变为 hap:
flutter build hap --debug如果命令名不完全一致(不同分支版本可能变化),先敲flutter build --help看有哪些可用的 target。构建完成后,hap 会落在类似entry/build/default/outputs/default/的目录下,文件名一般是entry-default-unsigned.hap或者entry-default-signed.hap,取决于你是否配置了签名。
这里要特别提醒:OpenHarmony 安装 hap 时必须要有签名,否则设备会拒绝安装。你看到unsigned字样时,千万不要硬着头皮往设备上装。
5.2 签名配置:自动签名优先
签名的方式有两种:一是用官方 IDE 创建调试证书并自动签名,二是手动生成证书,编辑build-profile.json5。我明确建议入门阶段用第一种,因为 OpenHarmony 的证书体系有p12(私钥)、cer(证书)、p7b(Profile)三件套,手动配置很容易出现三者不对应的问题,调试成本极高。
你在 IDE 里选择“自动签名”,它会帮你生成调试证书,并把配置写进工程。之后命令行构建时,只要工程文件里能看到签名信息,产出就会是带签名的 hap。切回终端执行flutter build hap --debug,再一次构建,拿到带签名的 hap,就可以进入安装环节了。
如果你的场景必须手动签名,记住一个检查思路:私钥、证书、Profile 文件三者间是绑定关系,报signature verification failed或provision profile not match时,通常不是命令问题,而是证书链对不上,重新生成整套调试证书往往比搜解法更快。
5.3 连接设备与调试命令
OpenHarmony 的模拟器或真机通过 hdc 连接。先看设备是否被识别:
hdc list targets有设备后,安装 hap:
hdc install /path/to/entry-default-signed.hap如果一切正常,你可以在设备上看到并打开这个应用。想直接进入调试模式,也可以尝试flutter run -d <deviceId>,它会部署并启动应用,理论上还支持热重载。不过坦白讲,Flutter for OpenHarmony 的热重载体验在不同版本上并不一致,偶尔按r或R不起作用。这时最实用的兜底方式是:
hdc shell aa start -b <bundleName> -a <abilityName>用 hdc 手动拉起来。另外日常看原生侧日志用:
hdc shell hilog和 Android 的adb logcat定位差不多。
6. M1 上最容易翻车的地方:排查链路与解决方案
6.1 Darwin arm64 与工具链架构不一致
这是 M1 上的头号大坑,我把它排在第一位。表现是:一切安装正常,但构建时冒出Unsupported option 'march=armv8-a'或compiler not found之类的问题,又或者干脆xcrun: error。
排查链路很固定。第一步,用file命令检查 JDK:
file /Library/Java/JavaVirtualMachines/*/Contents/Home/bin/java第二步,确认当前终端跑的是 ARM 版还是 Rosetta 翻译版。如果终端本身是 x86_64 外壳,你的 shell 环境变量可能都被翻译层污染了,安装什么原生的 ARM 软件都会被强制走 Intel 解释,你根本意识不到。检查方法:
uname -m输出arm64才是原生终端。如果你的终端是通过 Intel 版编辑器或老式 Rosetta 方式启动的,第一条处理建议不是修环境变量,而是换一个新的原生 ARM 终端或重启终端方式。
第三步,确认 Flutter fork 分支显示版本信息中有没有类似Flutter • Channel ohos • ...的正常输出,再看flutter doctor -v是否已经“看到” OpenHarmony SDK。
这三步都正常后,绝大多数架构类报错都能排除。
6.2 ohpm 依赖拉不下来
构建 Flutter for OpenHarmony 工程时,hvigor 会调用 ohpm 拉取 ArkTS 侧依赖。常见报错:
ohpm install: Error: Failed to resolve: @ohos/xxx排查思路是判断“源不可达”还是“版本不存在”。先看当前 ohpm 配置的 registry:
ohpm config get registry如果默认源不可用,可以配置一个可访问的镜像源地址,这是国内开发环境的标准操作。配置完成后,清除本地缓存重新安装:
ohpm cache clean rm -rf oh_modules ohpm install还有一类情况是官方模板里某些依赖版本消失了。这时不要随意指定新版本,而是检查 flutter_flutter 模板当前推荐的版本号,尽量保持一致。
6.3 签名报错的快速定位
Flutter 侧的签名报错一般表现为:
Error: failed to sign hap: [-1] verify sign config failed或者提示找不到证书文件。快速定位方式如下表:
| 报错特征 | 最可能原因 | 检查方法 |
|---|---|---|
| 提示找不到 .p12/.cer/.p7b 文件 | 配置文件路径写错 | 检查build-profile.json5中签名配置的路径,改成绝对路径先试 |
| 提示证书不匹配 | 三件套不是同一套证书的 | 到 IDE 里重新生成调试证书和 Profile |
| 提示 Profile 过期 | 调试证书有效期过了 | 更新 Profile,重新下载 |
| 安装到设备仍报签名校验失败 | 设备与签名 Profile 中的设备名单不匹配 | 重新注册设备调试证书 |
最省事的做法仍然是在 IDE 里重新触发一次自动签名,让 IDE 帮你重置三件套,然后再回命令行构建。不要试图手动编辑签名文件的 Base64 内容,字符串稍微多一点或少一个字母都是无效的。
6.4 首次构建慢到让人怀疑人生
M1 的 CPU 本身不弱,但首次构建 Flutter for OpenHarmony 工程可能慢得夸张,主要时间花在两件事上:一是 Flutter 引擎预编译产物的下载和解压,二是 hvigor 首次构建生成中间产物。如果你跳过flutter precache --ohos,首次构建甚至可能从源码编译引擎,那时间就按小时算了。
我给的建议是分两步。先单独做一次flutter precache --ohos,等它真正完成;再执行flutter build hap --debug。构建期间尽量不要同时开多个模拟器,M1 的 16G 内存同时跑 IDE、模拟器、构建工具、浏览器,非常容易触发系统 Swap,磁盘疯狂读写后整个构建变慢几倍。
如果你发现构建进程突然 OOM,可以检查 IDE 里 hvigor 编译器的 JVM 内存配置,适当调大堆内存。不过这是在“能用”的前提下优化,入门阶段不必一上来就碰。
6.5 版本对齐:比任何一条命令都重要
整个实战过程我最后悔的事情,就是一开始没有把“版本矩阵”固定下来。Flutter fork 的版本、OpenHarmony SDK 版本、IDE 版本、JDK 版本,这四个值之间是互相绑定的。我在某个版本组合下查到的命令,换到另一个组合可能就变了,比如flutter config参数、flutter create --platforms的参数、hvigor 的配置写法。
给你一个我自己的版本选型示意:
| 组件 | 我的推荐策略 | 说明 |
|---|---|---|
| OpenHarmony SDK | 使用 IDE 引导安装的稳定版 | 不要同时装多套,避免环境变量指错 |
| Flutter fork | 跟随官方 SIG 的 release 标签 | 不追 master,版本锁定到具体 tag |
| JDK | JDK 17 的 ARM64 原生版 | 除非工具链文档明确要求旧版 |
| macOS | 保持系统更新到当前稳定版本 | 避免因系统过旧导致的动态库问题 |
确定版本组合后,把安装命令、环境变量写进文档,团队其他人复制这套组合即可。很多“我明明照着教程做为什么报错”的情况,都是教程环境和你的环境版本不一致导致的。
7. 从 Demo 到能落地:后续路线怎么走
7.1 先搞清楚“插件适配范围”,再决定业务能不能用
跑通 Hello World 之后,很多人会立刻想把现有 Flutter 项目迁移过来,但这一步大概率会碰壁。原因在于 Flutter 生态里的大量第三方插件都依赖 Android/iOS 的原生实现,而 Flutter for OpenHarmony 并不天然兼容所有 pub.dev 插件。
你在pubspec.yaml里加一个依赖,如果插件内部使用了 Android 的MainActivity或 iOS 的AppDelegate桥接逻辑,在 OpenHarmony 平台上就缺少对应实现,轻则编译不通过,重则运行时报MissingPluginException。
评估插件是否可用,先看它是否支持 OpenHarmony 平台,再看它内部是不是只使用了 Dart 侧 API。如果必须用到某插件且它没有 OpenHarmony 适配,通常的出路有三条:找 OpenHarmony 生态里的原生插件替代;走 Flutter MethodChannel 自己封装 OpenHarmony 原生能力;修改插件源码增加 OpenHarmony 端实现。这三条路的成本依次递增。
所以上生产项目之前,第一件事是拉一张插件清单,逐个标注“可用/需适配/不可用”,而不是先搭架构。这也是我在这次实战里学到的最重要的评估方法论。
7.2 我建议的学习路线
如果你打算继续深入,我建议按这个顺序走:
- 跑通模板工程的 debug 构建、安装、日志查看;
- 验证 Flutter 常用组件在 OpenHarmony 上的渲染表现,比如 ListView、动画、路由;
- 做一次 MethodChannel 原生通信,调用一个 OpenHarmony 原生能力,比如获取设备信息或传感器数据,摸清 Dart 和 ArkTS 之间的桥接方式;
- 验证关键第三方插件的兼容性,再决定是否扩大使用;
- 最后才谈 release 构建、包体积和性能。
这个路线的好处是每一层都有明确的验证目标,不会在“能跑”和“能交付”之间突然跨出一大步。我自己就是在跑到第四步时发现最耗时间的不是环境,而是插件缺口,这个问题越早暴露越好。
整个环境搭下来,我最强烈的体会有两个。第一个是版本一致性远比任何一步操作都重要,你现在看到别人博客里的命令,很可能基于一套你完全不知道的版本组合,照抄前一定要先对齐版本矩阵。第二个是遇到报错先看日志头部和报错行号,不要直接拿错误码去搜索引擎硬搜,很多错误在 OpenHarmony 工具链里被包装过,表面错误码和真实原因差很远。这是踩过几次坑之后才养成的习惯,希望你能少走这一截弯路。