09 鸿蒙(HarmonyOS NEXT / OpenHarmony)平台开发
9.1 现状与原理
HarmonyOS NEXT(纯血鸿蒙,HarmonyOS 5 及以后)不再兼容 APK,应用必须是 HAP 包,原生开发语言为 ArkTS,UI 框架为 ArkUI。Google 官方 Flutter 不支持鸿蒙;可用的是 OpenHarmony SIG 社区(华为及合作伙伴主导)维护的分支 Flutter OH:
- 仓库:
gitcode.com/openharmony-sig/flutter_flutter(Gitee 有镜像)。 - 版本:跟随官方版本做适配,tag 形如
3.27.5-ohos-x.x.x(基于 3.27.4)、3.35.8-ohos-x.x.x(基于 3.35.7)、3.41.10-ohos-x.x.x(基于 3.41.9)。官方计划中 3.44、3.47 的适配版本分别预计在 2026/09、2026/12 发布,以仓库公告为准。鸿蒙版本通常落后官方 stable 1~2 个大版本。 - 3.35.7 起,引擎源码合并进同一仓库的
engine/目录。
原理(对照第 6 章):Framework(Dart)层基本不变,社区新增了:
Flutter Framework(Dart) 增加 TargetPlatform.ohos 等少量改动
Flutter Engine(C++) 新增 OHOS 平台 shell:
- 渲染到 ArkUI XComponent 提供的 NativeWindow
- VSync 来自鸿蒙 NativeVsync
- 输入、键盘、无障碍、字体对接鸿蒙 NDK
- GPU:OpenGL ES,新版本支持 Impeller(Vulkan)
Embedder(ArkTS + NAPI) flutter_ohos HAR 包:
FlutterAbility / FlutterPage / FlutterEngine
MethodChannel / EventChannel / PlatformView / Texture
插件接口 FlutterPlugin
宿主工程 ohos/ DevEco Studio 工程(hvigor 构建、ohpm 依赖)
可以看到架构与 Android 一一对应:FlutterActivity → FlutterAbility,FlutterView → FlutterPage(基于 XComponent),Java 插件 → ArkTS 插件,JNI → NAPI,Gradle → hvigor,Maven → ohpm。
9.2 环境搭建
1. 安装 DevEco Studio
从华为开发者联盟下载 DevEco Studio(自带 HarmonyOS SDK、ohpm、hvigor、node、hdc)。登录华为账号,用于自动签名。
2. 安装 Flutter OH SDK(与官方 Flutter 并存)
git clone https://gitcode.com/openharmony-sig/flutter_flutter.git ~/sdk/flutter_ohos
cd ~/sdk/flutter_ohos
git checkout 3.35.8-ohos-1.0.x # 选择最新稳定 tag,以仓库 Release 为准
推荐用 FVM 管理:把该目录放入 FVM 的版本目录(~/fvm/versions/3.35.8-ohos),项目内 fvm use 3.35.8-ohos。
3. 环境变量(macOS,~/.zshrc)
# DevEco Studio 工具链
export TOOL_HOME=/Applications/DevEco-Studio.app/Contents
export DEVECO_SDK_HOME=$TOOL_HOME/sdk
export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH
export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH
export PATH=$TOOL_HOME/tools/node/bin:$PATH
export PATH=$DEVECO_SDK_HOME/default/openharmony/toolchains:$PATH # hdc
# Flutter OH(注意:这个镜像地址与官方 Flutter 的不同)
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://flutter-ohos.obs.cn-south-1.myhuaweicloud.com
export PATH=$HOME/sdk/flutter_ohos/bin:$PATH
变量名和路径以 Flutter OH 仓库《环境搭建指导》为准,DevEco 版本升级后目录可能变化。
注意:
FLUTTER_STORAGE_BASE_URL必须指向华为云的鸿蒙引擎产物地址;官方 Flutter 则使用storage.flutter-io.cn。在两个 SDK 之间切换时,修改该值后要删除对应 SDK 的bin/cache并执行flutter clean,否则会报 Dart VM snapshot 版本不匹配。- hvigor 依赖
~/.npmrc中配置的华为 npm 源。 - 建议写一个切换脚本,或者只在 FVM 的项目级环境中设置,避免和官方 Flutter 互相污染。
4. 检查
flutter doctor -v # 应出现 HarmonyOS toolchain 一项
flutter devices # 连接鸿蒙真机(打开开发者模式和 USB 调试)或启动模拟器
hdc list targets
模拟器限制:仅支持 Apple Silicon Mac 和 Windows x64,不支持 Intel Mac;模拟器不支持 Vulkan,需要关闭 Impeller(参考仓库 FAQ)。真机是性能和兼容性测试的唯一依据。
9.3 创建工程 / 给现有工程加鸿蒙
# 新工程
flutter create --platforms ohos,android,ios --org com.example my_app
# 现有工程添加 ohos 目录
cd existing_app
flutter create --platforms ohos .
# 插件 / module
flutter create -t plugin --platforms ohos,android,ios my_plugin
flutter create -t module my_module
ohos/ 目录结构:
ohos/
├── AppScope/
│ ├── app.json5 # bundleName、versionCode/versionName、图标(≈ Manifest application)
│ └── resources/
├── entry/ # 主模块(≈ app module)
│ ├── src/main/
│ │ ├── ets/
│ │ │ ├── entryability/EntryAbility.ets # ≈ MainActivity
│ │ │ ├── pages/Index.ets # 承载 FlutterPage 的 ArkUI 页面
│ │ │ └── plugins/GeneratedPluginRegistrant.ets
│ │ ├── module.json5 # 权限、Ability 声明(≈ AndroidManifest)
│ │ └── resources/
│ ├── oh-package.json5 # 模块依赖(≈ build.gradle dependencies)
│ └── build-profile.json5
├── build-profile.json5 # 工程级:签名、products、compatibleSdkVersion
├── hvigorfile.ts # ≈ build.gradle
└── oh-package.json5 # 依赖 @ohos/flutter_ohos 等
9.4 构建与运行
flutter run -d <device> # 调试
flutter build hap --debug
flutter build hap --release # 产物在 ohos/entry/build/default/outputs/default/*.hap
flutter build app --release # 上架应用市场的 .app 包
- 签名:用 DevEco Studio 打开
ohos/目录,File → Project Structure → Signing Configs,勾选自动签名(需登录华为账号,真机调试必须签名)。签名信息写入ohos/build-profile.json5,发布签名材料不要提交到公开仓库。 - profile 模式需在
build-profile.json5中添加对应的buildModeSet。 - 也可以在 DevEco Studio 中直接运行
ohos工程(此时 Dart 产物需要先用flutter build hap生成),方便调试 ArkTS 代码和查看 hilog。
9.5 宿主入口代码
// ohos/entry/src/main/ets/entryability/EntryAbility.ets
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { BatteryPlugin } from '../plugins/BatteryPlugin';
export default class EntryAbility extends FlutterAbility {
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine);
GeneratedPluginRegistrant.registerWith(flutterEngine); // pubspec 中的插件
this.addPlugin(new BatteryPlugin()); // App 私有插件
}
}
// ohos/entry/src/main/ets/pages/Index.ets
import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos';
let storage = LocalStorage.getShared();
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS';
@Entry(storage)
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext;
@LocalStorageLink('viewId') viewId: string = '';
build() {
Column() {
FlutterPage({ viewId: this.viewId }) // 内部是 XComponent,Flutter 在上面绘制
}
}
// 系统返回手势/返回键交给 Flutter 的 Navigator 处理
onBackPress(): boolean {
this.context.eventHub.emit(EVENT_BACK_PRESS);
return true;
}
}
以上为 flutter create 生成的模板代码,不同 Flutter OH 版本略有差异,以本地生成的为准。
9.6 ArkTS 插件开发
Dart 侧代码与 Android/iOS 完全相同(同一个 MethodChannel('com.example/battery')),只需要补充 ArkTS 实现。
// ohos/entry/src/main/ets/plugins/BatteryPlugin.ets
import {
FlutterPlugin,
FlutterPluginBinding,
MethodCall,
MethodCallHandler,
MethodChannel,
MethodResult,
} from '@ohos/flutter_ohos';
import batteryInfo from '@ohos.batteryInfo';
export class BatteryPlugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return 'BatteryPlugin';
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel = new MethodChannel(binding.getBinaryMessenger(), 'com.example/battery');
this.channel.setMethodCallHandler(this);
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.channel?.setMethodCallHandler(null);
this.channel = null;
}
onMethodCall(call: MethodCall, result: MethodResult): void {
switch (call.method) {
case 'getLevel':
result.success(batteryInfo.batterySOC); // 电量百分比
break; // 不要漏 break,否则会落到 default 再次回复
default:
result.notImplemented();
}
}
}
较老的 Flutter OH 版本中,部分类型需要从子路径导入,例如 @ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin、@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel。如果从包根路径导入时报错,按 IDE 提示改为子路径。
异步调用系统 API 的写法:
onMethodCall(call: MethodCall, result: MethodResult): void {
if (call.method === 'getPrefs') {
const key: string = call.argument('key');
this.store.get(key, '')
.then((v) => result.success(v))
.catch((e: Error) => result.error('PREFS_ERROR', e.message, null));
return; // 每个分支只回复一次
}
result.notImplemented();
}
需要 UIAbility(拉起页面、申请权限)时实现 AbilityAware,在 onAttachedToAbility(binding) 中拿到 binding.getAbility(),与 Android 的 ActivityAware 对应。
独立插件包的鸿蒙实现
flutter create -t plugin --platforms ohos my_plugin
my_plugin/
├── lib/my_plugin.dart
├── android/ ios/
└── ohos/ # 一个 HAR 模块(≈ Android library)
├── index.ets # 导出插件类
├── oh-package.json5 # 依赖 @ohos/flutter_ohos
└── src/main/ets/components/plugin/MyPlugin.ets
flutter:
plugin:
platforms:
ohos:
pluginClass: MyPlugin
ArkTS 与 Kotlin 的差异(写插件时最常见的报错)
ArkTS 是 TypeScript 的严格子集:
- 不允许
any/unknown,所有变量和返回值需要明确类型。 - 不支持对象字面量直接作为类型(需要先声明
class/interface)。 - 不支持解构赋值、
for..in、动态增删属性。 - 系统能力以
@ohos.xxx/@kit.XxxKit模块导入,异步 API 多为 Promise 风格。 - 主线程是 ArkTS UI 线程,耗时任务用
taskpool或worker。
9.7 PlatformView(嵌入 ArkUI 组件)
Dart 侧:
OhosView( // Flutter OH 新增的 Widget
viewType: 'com.example/native_button',
creationParams: const {'text': 'from flutter'},
creationParamsCodec: const StandardMessageCodec(),
);
ArkTS 侧(结构示意,API 细节以所用版本的示例工程为准):
import common from '@ohos.app.ability.common';
import { PlatformView, PlatformViewFactory, StandardMessageCodec, Params } from '@ohos/flutter_ohos';
@Component
struct NativeButton {
@Prop params: Params;
build() {
Button('ArkUI 原生按钮').onClick(() => { /* ... */ })
}
}
@Builder
function buildNativeButton(params: Params) {
NativeButton({ params: params })
}
class NativeButtonView extends PlatformView {
getView(): WrappedBuilder<[Params]> {
return new WrappedBuilder(buildNativeButton);
}
dispose(): void {}
}
export class NativeButtonFactory extends PlatformViewFactory {
constructor() {
super(StandardMessageCodec.INSTANCE);
}
public create(context: common.Context, viewId: number, args: Object): PlatformView {
return new NativeButtonView();
}
}
// 插件 onAttachedToEngine 中注册
// binding.getPlatformViewRegistry()?.registerViewFactory('com.example/native_button', new NativeButtonFactory());
外接纹理:通过 binding.getTextureRegistry() 注册纹理,原生侧把 NativeWindow/surfaceId 交给播放器或相机渲染,Dart 端用 Texture(textureId:) 显示。video_player、camera 的鸿蒙实现就是这样做的,可以直接参考其源码。
9.8 三方库适配
pub.dev 上的包分两类:
- 纯 Dart 包(dio、provider、riverpod、go_router、freezed、intl…):不含原生代码,在鸿蒙上直接可用。
- 含原生代码的插件(shared_preferences、path_provider、camera、image_picker、webview_flutter、permission_handler…):pub.dev 版本没有 ohos 实现,运行时会抛
MissingPluginException。
处理方式:
# pubspec.yaml:用社区适配版本覆盖
dependency_overrides:
shared_preferences:
git:
url: https://gitcode.com/openharmony-sig/flutter_packages.git
path: packages/shared_preferences/shared_preferences
ref: <tag 或 commit> # 务必锁定,避免随默认分支变化
path_provider:
git:
url: https://gitcode.com/openharmony-sig/flutter_packages.git
path: packages/path_provider/path_provider
ref: <tag 或 commit>
permission_handler:
git:
url: https://gitcode.com/openharmony-sig/flutter_permission_handler.git
path: permission_handler
ref: <tag 或 commit>
查找已适配的库:
openharmony-sig/flutter_packages:官方 plugins 的鸿蒙实现(camera、video_player、webview_flutter、url_launcher、image_picker、shared_preferences、path_provider 等)。openharmony-sig/fluttertpc_*:热门三方插件的适配(命名形如fluttertpc_<包名>)。- Flutter OH 仓库中的“三方库适配列表”文档。
没有适配的插件:
- 联邦插件:新建一个
xxx_ohos包实现xxx_platform_interface,在 pubspec 中default_package关联。 - 非联邦插件:fork 后增加
ohos/目录和pubspec.yaml中的ohos平台声明。 - 依赖 C/C++ 的 FFI 插件:用鸿蒙 NDK(CMake 工具链
ohos.toolchain.cmake,arm64-v8a,musl libc)重新编译 so。Android NDK 编译的 so 不能直接用。 - 依赖 Android/iOS 原生 SDK 的插件(地图、推送、支付、登录、统计):需要接入该厂商的鸿蒙版 SDK(HAR 包),用 ArkTS 重写插件实现。这是鸿蒙适配中工作量最大的部分,排期时要逐个评估。
9.9 Add-to-App:现有鸿蒙原生工程接入 Flutter
flutter create -t module my_module
cd my_module
flutter build har --release # 产出 flutter.har(引擎)和 my_module.har 等
宿主工程 oh-package.json5:
{
"dependencies": {
"@ohos/flutter_ohos": "file:./har/flutter.har",
"@ohos/flutter_module": "file:./har/my_module.har"
}
}
在原生 UIAbility 中初始化 FlutterManager,在 ArkUI 页面中使用 FlutterEntry 承载 Flutter(≈ Android 的 FlutterFragment):
// EntryAbility.ets(宿主)
import { FlutterManager } from '@ohos/flutter_ohos';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
FlutterManager.getInstance().pushUIAbility(this);
}
onDestroy(): void {
FlutterManager.getInstance().popUIAbility(this);
}
onWindowStageCreate(windowStage: window.WindowStage): void {
FlutterManager.getInstance().pushWindowStage(this, windowStage);
windowStage.loadContent('pages/Index');
}
onWindowStageDestroy() {
FlutterManager.getInstance().popWindowStage(this);
}
}
// FlutterHostPage.ets(宿主中的某个页面)
import { FlutterEntry, FlutterPage, FlutterView } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '@ohos/flutter_module';
@Entry
@Component
struct FlutterHostPage {
private flutterEntry?: FlutterEntry;
private flutterView?: FlutterView;
aboutToAppear() {
this.flutterEntry = new FlutterEntry(getContext(this)); // 可传 initialRoute 参数
this.flutterEntry.aboutToAppear();
this.flutterView = this.flutterEntry.getFlutterView();
GeneratedPluginRegistrant.registerWith(this.flutterEntry.getFlutterEngine()!);
}
aboutToDisappear() { this.flutterEntry?.aboutToDisappear(); }
onPageShow() { this.flutterEntry?.onPageShow(); }
onPageHide() { this.flutterEntry?.onPageHide(); }
build() {
FlutterPage({ viewId: this.flutterView?.getId() })
}
}
接入 API 在不同 Flutter OH 版本之间有调整,以对应版本仓库中的 add-to-app 文档和示例为准。混合栈可使用已适配鸿蒙的 flutter_boost。
9.10 一套代码中区分鸿蒙
import 'dart:io' show Platform;
// 用字符串比较,代码在官方 Flutter(没有 ohos 枚举值)下也能编译
bool get isOhos => !kIsWeb && Platform.operatingSystem == 'ohos';
注意:TargetPlatform.ohos 只存在于 Flutter OH SDK 中,如果直接写进业务代码,用官方 Flutter 构建 Android/iOS 时会编译失败。
9.11 鸿蒙端常见问题
| 问题 | 原因 / 解决 |
|---|---|
MissingPluginException |
插件没有 ohos 实现 → 用 dependency_overrides 替换为适配版或自行适配 |
| 两套 SDK 版本不一致 | 官方 Flutter(3.47)与 Flutter OH(3.35)同时维护:Dart 代码必须兼容较低版本;第三方包版本取两边都能解析的版本;CI 分两条流水线。或者三端统一使用 Flutter OH 的版本(它同样支持 Android/iOS 构建),降低维护成本 |
pubspec.lock 冲突 |
两个 SDK 解析出的锁文件不同,可分别生成,或以 Flutter OH 版本为基准统一 |
| Dart VM snapshot 版本不匹配 | FLUTTER_STORAGE_BASE_URL 设置错误,修正后删除 bin/cache 并 flutter clean |
hvigor 构建失败 / 找不到 module.json5 |
检查 ~/.npmrc、DevEco 版本、插件工程结构是否为新版格式(参考仓库“插件工程结构升级”文档) |
| 真机安装失败(签名) | DevEco 中重新自动签名;设备开启开发者模式;pkcs7 校验失败时参照 FAQ 设置 persist.bms.ohCert.verify |
| 日志看不到 | 使用 hdc hilog,或在 DevEco 的 Log 面板按 domain/tag 过滤;日志太多时会被限流丢失 |
| 权限申请 | module.json5 的 requestPermissions 中声明,user_grant 类权限还需运行时调用 abilityAccessCtrl.requestPermissionsFromUser;使用适配版 permission_handler |
| 字体与行高差异 | 鸿蒙默认字体为 HarmonyOS Sans,与 Android 的 Roboto/思源黑体行高不同,关键 UI 可内置统一字体(见第 10 章) |
| 返回手势无效 | 检查 Index.ets 的 onBackPress 是否把事件转发给 Flutter |
| 输入法/键盘遮挡 | 使用新版本 Flutter OH;页面设置 resizeToAvoidBottomInset;必要时在 ArkTS 侧设置 KeyboardAvoidMode |
| 模拟器黑屏 | 模拟器不支持 Vulkan,关闭 Impeller;Intel Mac 不支持模拟器 |
| 性能比 Android 差 | 确认用 release 包测试;确认是否开启 Impeller;排查 PlatformView 数量;见第 11 章 |
| 华为应用市场上架 | 需要 AGC 发布证书和 Profile;产物为 .app(flutter build app);隐私声明、权限用途说明要完整 |
9.12 鸿蒙适配工作量评估清单
- 列出
pubspec.yaml中所有带原生代码的插件(flutter pub deps后查看是否有android/ios/目录)。 - 逐个查询社区是否已适配,记录对应仓库和版本。
- 未适配的插件:评估是否有鸿蒙版原生 SDK、是否需要用 ArkTS 重写。
- App 自己的 MethodChannel:逐个用 ArkTS 实现。
- C/C++ so:用鸿蒙 NDK 重新编译。
- 检查代码中
Platform.isAndroid/Platform.isIOS分支,补充鸿蒙分支。 - 真机全量回归:布局、字体、键盘、返回、权限、分享、推送、支付、登录、深链。
动手练习
- 安装 DevEco Studio 与 Flutter OH,在鸿蒙真机运行计数器 Demo。
- 给第 7 章的电量插件补充 ArkTS 实现,一套 Dart 代码在 Android 与鸿蒙上都能拿到电量。
- 在一个使用了
shared_preferences和path_provider的工程中,通过dependency_overrides让其在鸿蒙上运行。