从去年底开始,我就在琢磨一件事:项目里积攒了上千段家庭影像素材,散落在手机相册、旧硬盘和云盘各个角落,每次想翻一段出来都像在仓库里找螺丝。于是就有了“忆影 · MemoPlay”——一个基于 Flutter × HarmonyOS 6.0 的沉浸式视频播放器,定位很简单:把回忆变成可以沉浸播放、快速检索、多端同步的个人影像库。这个项目从开发到真机适配花了我将近两个月,中间踩的坑比过去一年写的 Bug 都多。如果你也打算在鸿蒙生态里用 Flutter 做视频类应用,这篇文章应该能帮你省下至少一周的时间。
项目刚开始走上 Flutter 这条路,其实没什么悬念——团队里没有人会原生开发鸿蒙的 ArkTS,而项目本身又要覆盖 Android 和日后可能存在的桌面端,Flutter 几乎是唯一能在不膨胀团队的前提下把多端 UI 撑起来的框架。真正需要反复权衡的是:播放器内核用现成插件还是自己封装?本地数据库怎么和内嵌的播放器状态做联动?HarmonyOS 6.0 上到底哪些能力能直接调、哪些必须走平台通道? 这些问题,每一个都能单独写几千字的复盘。我把整个项目的演进过程拆成几个模块,逐个说清楚做了什么、为什么这么做、以及最后在鸿蒙真机上验证的结果是什么。
1. 为什么在 HarmonyOS 6.0 上做播放器,我坚持用 Flutter
先交代一下背景。HarmonyOS 6.0 目前主推的原生开发语言是 ArkTS + ArkUI,生态位已经比前几代成熟不少,新的 API 版本对多模态交互、方舟引擎的调度也都做得更细。但问题也很现实:我的核心诉求是在存量代码和未来多端能力之间取一个平衡,而不是把时间全砸进一门新语言和一套新 UI 体系的磨合里。Flutter 的渲染引擎是自绘的,不依赖系统组件树,所以它在鸿蒙上的适配路线是"框架移植 + 平台通道对接",这意味着 UI 层面理论上可以做到接近完全复用。
不过"理论上"和"实际上"之间隔着一条鸿沟。我最初做了一个很小的验证 Demo:一个 Flutter 页面 + 一个按钮 + 一个 Text 控件,在 Android 模拟器上跑通只花了半小时,但把这套东西编译成 HarmonyOS 6.0 的 HAP 包,光环境搭建就折腾了一天。这里就不得不提到 Flutter 的鸿蒙分支(OpenHarmony 适配版)和官方主干是两套不同的构建链路:官方主干的产物是 APK,而鸿蒙需要的是 HAP,不是你装个 Flutter SDK 就能直接出包,必须依赖 OpenHarmony 的 Flutter 引擎 SDK 和配套的编译工具链。
我的做法是这样的:
- 单独维护一份
ohos工程目录,不直接合并进主 Flutter 工程,而是通过flutter_ohos工具把 Dart 代码和原生壳工程编到一起; - 在
pubspec.yaml里固定 Flutter SDK 版本范围,避免鸿蒙工具链和主干版本产生 ABI 冲突; - 所有涉及系统能力(图库、IAP、数据库)的调用,统一走 MethodChannel,避免直接依赖尚未完全兼容的 Flutter 社区插件。
这一个决策后来被证明是明智的。因为社区里大量播放器插件(比如 video_player、chewie 这类)底层还是走 Android 的 ExoPlayer 或 Media3,在鸿蒙上根本没有对应的原生实现。就算强行编译通过,运行起来也是"能出画面、没声音 / 有声音、黑屏"这种诡异状态,调试成本极高。
另外还有个容易被忽略的细节:HarmonyOS 6.0 的权限模型和 Android 不完全一致,尤其是读写媒体库、访问图库缩略图这些能力,API 名称和回调时机的差异很大。在开发前期如果不把这些梳理清楚,后面一旦进入真机联调阶段,会发现一半以上的 Bug 都出在权限申请的时序上。
所以我给这个项目定了一个原则:Flutter 负责能复用的 UI 和业务逻辑,鸿蒙原生负责能力供给,两者之间的边界必须清晰。后面所有的播放器内核、数据库、同步逻辑都是在这个边界上长出来的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 播放器内核选型:比"能播"更重要的三件事
视频播放器这个品类,表面上是"放视频",实际上拼的是三件事:格式兼容性、渲染性能、状态恢复能力。任何一件做不好,都会直接毁掉"沉浸式"这三个字的体验。
我一开始图省事,直接在 Flutter 里用了社区常见的 video_player 插件,在 Android 上测试一切正常。但移植到 HarmonyOS 6.0 之后就露馅了——这个插件的底层是调用 Android 的 MediaPlayer,到了鸿蒙上根本没有对应的原生实现。后来我查了一下 OpenHarmony 的 Flutter 生态,发现当时官方推荐的方案有两种:
| 方案 | 底层实现 | 优点 | 缺点 |
|---|---|---|---|
| video_player 自行移植 | 通过 PlatformView 对接鸿蒙 AVPl |
