这是一篇真实的 Flutter 到 HarmonyOS / OpenHarmony 迁移复盘:从迁移前分析,到工具链、白屏、MissingPlugin、WebView、图片选择、签名上架,再到最后沉淀出一套可复用迁移 Skill。

skill的git仓库 https://gitee.com/hfqf1234/figo-flutter-harmonyos-migration-skill

先说结果

这次迁移最让我意外的,不是“鸿蒙版本终于跑起来了”,而是整个过程推进得非常快。

从可见提交记录看:

  • 某天上午 09:06,开始出现第一批 OHOS 迁移提交。

  • 某天下午 14:31,核心鸿蒙功能迁移完成。

  • 核心迁移跨度约 5 小时 24 分钟

  • 第二天下午 13:15,进一步把迁移经验沉淀成可复用 Skill。

  • 从核心迁移启动到 Skill 沉淀,整体跨度约 28 小时 8 分钟

最终跑通的不是一个空壳 Demo,而是已有 Flutter App 的真实业务链路:启动、登录、首页、客户列表、本地缓存、WebView、图片选择、拍照、发布包、应用市场问题处理,以及权限合规补充。

一开始,我就没把它当成“重写”

很多人听到“适配鸿蒙”,第一反应是:是不是要重写一套?

如果放在去年,我可能也会犹豫。

那时候很多链路还没有现在这么顺,遇到插件不支持、平台工程报错、原生通道缺实现,往往要靠人一点点翻文档、试版本、猜边界。迁移不是不能做,但过程会更重,也更容易卡在某个细节里。

今年明显不一样。

一方面,AI 开发能力让排查过程变得连续。Codex 可以读工程、看日志、找调用链、改代码、跑验证,再把处理过程沉淀成文档和 Skill。很多过去需要来回切换上下文的工作,现在可以在同一条迁移线上持续推进。

但另一方面,也是更核心的一点:Flutter 生态本身已经给了迁移足够好的底座。

如果一个 Flutter 项目已经把 UI、路由、状态、网络、国际化、主题、业务模型和大部分业务逻辑沉淀在跨平台工程里,真正应该做的就不是重写页面,而是先确认一件事:

现有业务代码能不能继续复用?平台差异能不能被压到足够底层?

这次项目的基础还不错:页面、路由、状态管理、网络层、本地缓存、上传、平台能力,都已经有一定抽象。也就是说,鸿蒙迁移的主线不是把每个页面重做一遍,而是在 Flutter 生态已经提供的复用基础上,把那些“不适合直接跨平台”的部分补上:

  • 工具链。

  • 平台工程。

  • Flutter 插件缺口。

  • 本地存储。

  • WebView。

  • 图片选择和拍照。

  • 权限说明。

  • 签名和发布包。

这个判断很重要。

一旦方向是“复用业务 + 补平台能力”,迁移就不再是无边无际的重写,而是一组可以被拆解的问题。

真正提速的是迁移前分析

这次迁移能推进得快,并不是因为一开始就直接开干。

相反,真正节省时间的,是动手前先做了一轮迁移分析。

当时先拆了几个问题:

  • 当前 Flutter 项目能不能直接生成 ohos/ 平台工程?

  • Flutter / Dart / 依赖约束是否匹配 OHOS Flutter?

  • 哪些依赖是纯 Dart 或纯 UI,风险很低?

  • 哪些依赖需要 OHOS 插件实现,风险中等?

  • 哪些能力强依赖原生 SDK、FFI、设备能力,必须专项处理?

  • 代码里有没有 Android / iOS 专属 import、MethodChannel、平台字段、权限申请和 SDK 初始化?

  • 首版到底要保留哪些功能,哪些可以先降级,哪些可以暂缓?

这一步很像迁移前的地形扫描。

如果不先做这件事,很容易一上来就掉进某个插件坑里,然后忘了迁移的主目标其实是:先让业务主链路跑起来。

最后迁移路线被拆成了几个阶段:

P0:空壳工程与构建验证
P1:核心编译适配
P2:核心业务闭环
P3:平台能力补齐
P4:签名、包体和上架

这样做的好处是,每一阶段都有自己的目标,不会让后面阶段的问题提前阻塞前面阶段。

比如蓝牙打印、推送、扫码、崩溃上报这些能力都重要,但它们不应该阻塞“应用能启动、能登录、能进入首页、核心列表有数据”。

这就是速度的来源之一:不是把所有问题同时解决,而是把问题放回它应该出现的阶段。

第一关:工具链隔离

鸿蒙迁移的第一个坑,不在代码里,而在工具链里。

Android / iOS 仍然要使用官方 Flutter,鸿蒙则需要 OHOS Flutter。如果直接把 OHOS Flutter 覆盖原来的 Flutter 环境,很容易把已有发版链路搞乱。

所以第一步就是隔离:

  • 官方 Flutter 继续服务 Android / iOS。

  • OHOS Flutter 单独安装。

  • DevEco Studio 和 HarmonyOS SDK 单独配置。

  • 用项目级脚本临时切换环境。

这一步看起来不性感,但它决定了后面能不能安心往前冲。

因为只要 Android / iOS 发版链路不被破坏,鸿蒙迁移就可以大胆试错;出了问题,影响范围也只在 OHOS 构建链路里。

【图片占位 4:工具链或构建命令截图】

第二关:先让它构建

生成 ohos/ 平台工程之后,马上进入构建问题。

这类问题通常很杂:Dart 版本、Flutter SDK、DevEco API、Hvigor、OHPM、HAR、HAP、第三方依赖,每一个都可能卡住。

这次遇到过两个典型问题:

一个是 Dart / Flutter 版本不匹配。旧的 OHOS Flutter 分支带的 Dart 版本太低,无法满足现有 Flutter 工程约束,只能切换到更合适的 OHOS Flutter 版本。

另一个是 DevEco SDK API 兼容问题。Flutter OHOS embedding 使用了更高 API 的 Autofill 类型,而当前 SDK 编译不过。最后的处理方式不是手工改一次算一次,而是把补丁沉淀成脚本,让构建前可以重复执行。

这个经验很朴素:

迁移过程中,凡是会重复踩的构建坑,都要脚本化。

一次手工修改是救火;脚本化之后,才算进入工程化。

第三关:能安装,但白屏

构建通过之后,并不代表应用能跑。

模拟器里打开应用,白屏。

这类问题最容易让人焦躁,因为它不像编译错误那样明确告诉你哪一行挂了。白屏只是在告诉你:Flutter 树没有正常挂载,或者首帧之前某个初始化卡住了。

排查顺序就变得很关键:

  1. runApp() 有没有执行。

  2. 首帧前有没有平台调用。

  3. 系统栏、方向、插件初始化有没有在 OHOS 上阻塞。

  4. 隐私协议、登录态恢复、推送、崩溃上报是不是太早执行。

最后定位到系统 UI 配置在 OHOS 启动阶段存在阻塞风险,于是在鸿蒙平台跳过相关 SystemChrome 调用,让 Flutter Widget 树先挂载。

这一步跑通后,登录页终于出来了。

看到登录页的那一刻,迁移就从“理论上可行”变成了“可以继续打穿”。

第四关:MissingPlugin 不是偶发,是必经之路

登录时很快遇到了经典错误:

MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

这不是某个小 bug,而是 Flutter 迁移鸿蒙时非常核心的一类问题:

Flutter 插件在 Android / iOS 上可用,不代表在 OHOS 上也有实现。

shared_preferences 如此,安全存储如此,很多平台能力都如此。

处理思路不是在业务页面里写一堆 if 判断,而是回到底层抽象:

  • 普通 KV 存储先做 OHOS 兜底。

  • 安全存储先保证登录链路能走通。

  • 长期方案再接鸿蒙安全存储能力。

这里有一个取舍:早期为了验证主链路,可以接受某些能力先降级;但必须在文档里写清楚这是 P0/P1 方案,不要把“临时兜底”误认为“长期实现”。

第五关:客户列表没数据,问题不是“鸿蒙没有 SQLite”

客户管理列表进入后没数据。

第一反应很容易是:是不是鸿蒙没有 SQLite?

后来确认,这个判断不准确。

鸿蒙系统本身有关系型数据库能力,可以通过 RDB 执行 SQL。真正的问题是 Flutter 侧原来的 drift + sqlite3 + sqlite3_flutter_libs + path_provider 这一套链路,在 OHOS 上没有完整打通。

所以解决方案不是放弃本地缓存,而是换一条路:

  • Flutter 业务层继续依赖缓存抽象。

  • Android / iOS 继续使用原来的 Drift 实现。

  • OHOS 侧通过 ArkTS 插件桥接系统 RDB。

这样一来,客户列表、登录后同步、本地搜索、车牌/VIN 查客户这些业务路径,都不需要大面积改页面。

这一步给我的经验很深:

插件不可用,不等于系统能力不存在。

迁移时不要只盯着 Flutter 插件,要往下看一层:系统有没有原生能力?能不能通过 MethodChannel 或 ArkTS 插件桥接?业务层能不能继续保持干净?

第六关:WebView 要从 Android 依赖里解耦

工作台顶部 H5、隐私政策、用户协议、报表页面,都依赖 WebView。

这类页面在业务上看起来只是“打开一个 H5”,但工程上常常埋着 Android 专属配置。如果 Dart 文件里直接 import Android WebView 实现,OHOS 编译就会失败。

所以先做了一层兼容收口:

  • 页面不直接依赖 Android WebView 类型。

  • Android 专属配置放进兼容层。

  • OHOS 接入自己的 WebView 实现。

最后工作台顶部 H5 能打开,协议页能打开,说明核心 H5 容器链路打通了。

这类问题的关键词还是一个:边界。

跨平台迁移最怕页面里到处散落平台细节。一旦平台差异被收口,后面换实现就轻很多。

【图片占位 5:工作台顶部 H5 在鸿蒙端运行截图】

第七关:图片选择、拍照和上传

图片能力是业务 App 很难绕开的部分。

客户头像、反馈附件、商品图片、车辆照片、门店图片、OCR 图片,背后都需要相册或相机。

OHOS 下如果没有现成插件,就自己补 ArkTS 插件:

  1. 打开系统图片选择器。

  2. 获取用户选择的图片 URI。

  3. 复制到应用 cache 目录。

  4. 返回 Flutter 可读取的本地路径。

  5. 继续复用原有压缩、上传、回填流程。

这一步的关键不是“写了一个图片插件”,而是让所有图片入口继续走统一 Image Gateway。

这样未来再补相机、压缩、权限、上传失败处理,只需要在能力层增强,不需要每个页面单独补一次。

【图片占位 6:图片选择或反馈附件截图】

第八关:能上架,还要能过审

技术迁移跑通后,还有发布迁移。

鸿蒙市场提交的不是 HAP,而是 .app。签名、profile、bundleName、设备类型任何一项不匹配,都可能报错。

迁移过程中遇到过几类问题:

  • 上传包不是 HarmonyOS 应用后缀。

  • 非法软件包。

  • 包名与应用不匹配。

  • 设备类型需要支持手机、平板、PC/2in1。

这些问题没有什么捷径,就是按发布链路逐项校验:

  • 确认 .app 产物。

  • 确认证书和 profile。

  • 确认 bundleName 与后台一致。

  • 确认设备类型。

  • 确认没有手工拆包破坏签名。

还有一个很容易被忽略的问题:权限合规。

应用审核反馈相机权限申请时,没有同步告知使用目的。这个问题不是隐私政策里写了就够,运行时申请权限前也要明确告诉用户:申请什么权限、用于什么功能、做什么用途,并且说明不能自动消失。

最后我们把权限用途说明统一放到权限服务里,而不是散落在页面里。这样客户反馈拍照、头像、车辆照片、商品图片、OCR 等入口,都能被统一覆盖。

为什么能这么快?

我复盘了一下,这次能在空闲时间里快速推进,主要不是因为“鸿蒙迁移很简单”,而是因为没有一开始就陷入重写。

真正提升效率的是几个判断:

1. 先分级,再动手

迁移前先把依赖和能力分级:

  • 低风险:纯 Dart、纯 Flutter UI、模型、日期格式、状态管理,优先保留。

  • 中风险:本地存储、权限、WebView、图片选择、外链、分享,需要查 OHOS 适配或做降级。

  • 高风险:SQLite 原生链路、扫码、蓝牙打印、推送、崩溃上报、地图、支付、厂商 SDK,单独专项处理。

这个分级让迁移不会被“所有东西看起来都有风险”吓住。

低风险直接前进,中风险找替代,高风险放进专项计划。节奏一下就清楚了。

2. 先打主链路,不追求一步到位

第一目标不是完美,而是:

  • 能构建。

  • 能启动。

  • 能登录。

  • 能进入首页。

  • 核心列表有数据。

  • 关键页面能打开。

主链路通了,后面每个问题都有落点。

3. 页面尽量不动,平台能力往底层收

如果每个页面都加 OHOS 判断,迁移会很快失控。

这次更有效的方式是:

Flutter 页面 -> 业务 Controller -> 平台网关 -> Android / iOS / OHOS 实现

页面只关心“我要选图片”“我要打开 WebView”“我要读缓存”,不关心底层是 Android 插件、iOS 插件,还是 OHOS ArkTS 插件。

4. 把临时方案和长期方案分开

有些地方为了快速验证,可以先降级,比如登录态内存兜底。

但必须明确写出来:

  • 这是为了跑通 P0/P1 主链路。

  • 它不是长期安全存储方案。

  • 后续要替换成正式鸿蒙能力。

这个边界能避免后续技术债变成“没人知道为什么这么写”。

5. 每个坑都要沉淀成检查项

构建坑沉淀成脚本。

发布坑沉淀成清单。

权限坑沉淀到统一服务。

平台能力坑沉淀到网关抽象。

这些东西不只是为了这次迁移,也是为了下一次升级 Flutter、升级 DevEco、重新打包、重新过审时,不再从零开始排查。

最后沉淀:一个 Flutter 鸿蒙迁移 Skill

这次迁移结束后,我没有只留下一份复盘文档,而是把方法进一步提炼成了一个可复用的 Skill:

figo-flutter-harmonyos-migration-skill

它主要覆盖:

  • Flutter 到 HarmonyOS / OpenHarmony 的迁移前可行性评估。

  • 低 / 中 / 高风险依赖分级。

  • Android / iOS 平台耦合点扫描。

  • P0-P4 迁移路线和阶段验收标准。

  • 首版功能保留、降级和暂缓取舍。

  • 官方 Flutter 与 OHOS Flutter 工具链隔离。

  • ohos/ 平台工程生成和检查。

  • 第一轮构建命令和构建阻塞排查。

  • 白屏和首帧问题定位。

  • MissingPluginException 处理思路。

  • 本地存储、SQLite/RDB、WebView、图片选择、拍照适配。

  • 权限用途说明和应用市场合规。

  • HAP、HAR、HSP、.app、签名、bundleName、设备类型检查。

  • 迁移后的验证清单。

里面还放了两个参考脚本:

scripts/harmonyos_flutter_env.sh
scripts/patch_flutter_ohos_api24_autofill.sh

一个用于展示如何局部切换 OHOS Flutter / DevEco 工具链;另一个用于展示如何把确认过的 SDK 兼容补丁沉淀为可重复执行的构建前脚本。

我觉得这件事比“这次迁移成功”更重要。

因为真正有价值的工程经验,不应该只停留在某一次问题解决里。它应该被压缩成下一次可以直接调用的方法。

写在最后

这次 Flutter 鸿蒙迁移给我的最大感受是:

速度不是靠莽出来的。

速度来自迁移前分析,来自风险分级,来自边界清楚,来自先主链路后细节,来自把平台差异压到底层,来自每解决一个坑就顺手把它变成脚本、清单或 Skill。

从某天上午 09:06 到当天下午 14:31,核心迁移跨度约 5 小时 24 分钟。

从开始迁移到沉淀出 Skill,整体跨度约 28 小时 8 分钟。

放在连续工作里,这个数字已经不错;放在空闲时间里,它更说明一件事:

只要架构边界足够清楚,AI 协作足够聚焦,Flutter 到鸿蒙的迁移完全可以比想象中更快。

下一次再遇到类似迁移,我不会从“怎么开始”重新想一遍。

我会直接打开那份 Skill。

skill的git仓库

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐