flutter_channel_json 鸿蒙 Flutter 插件适配实测,补齐 ArkTS 四字段契约模型与 MethodChannel 通道实现,Dart 侧零改动,打通四端统一 JSON 返回
开发工具: 华为云码道
DevEco Studio:
26.0.0(26.0.0.851)原始仓库: josercc/flutter_channel_json(GitHub)
本文配套仓库: oh-flutter/flutter_channel_json(AtomGit)
flutter_channel_json 用于规范 Flutter MethodChannel 两端的数据传输格式:它定义了一套 code / message / data / success 四字段契约,并给出 Dart、Android(Kotlin)、iOS(Swift)与 OpenHarmony(ArkTS)四端的一致实现,通常与 flutter_channel_event_bus 搭配使用。本文以 flutter_channel_json 1.0.1 为例,介绍源码准备、OHOS 平台实现、Example 宿主工程配置和真机运行验证。

这是一个插件库:Dart 层保持零改动,适配工作集中在 ArkTS 侧的数据模型与唯一的方法通道上。文中的代码以提交 029804c4f9205ecb21cbcc7bfc6a09fdc3437835 为参考,对应 TAG 1.0.1-ohos-1.0.0-beta.1;文中所有截图均为真机实拍。

一、插件简介与适配目标
MethodChannel 的消息体在 Flutter 侧是 Map、在原生侧是各自的字典类型,两端对「这条消息长什么样」没有天然约束。flutter_channel_json 把消息格式钉死为四字段:
code : int 状态码,成功固定 200,失败默认 500
message : String 消息
data : T? 数据
success : bool 是否成功
并为每一端提供同构的实现:Dart 的 FlutterChannelJson<T>、Kotlin 与 Swift 的同名模型、以及本次适配补齐的 ArkTS 模型。原生侧把结果按契约封好回传,Dart 侧统一用 fromJsonString 解析,两端不再各写一套解析逻辑。
需要特别说明的是上游的一个设计事实:Dart 层没有实现任何通道调用。它只提供数据契约与编解码,原生侧(Android / iOS)只保留了插件模板方法 getPlatformVersion。因此本次适配的核心不是「实现一堆方法」,而是:
- 把 ArkTS 侧的数据模型与 Dart 契约逐字段对齐;
- 让唯一的通道方法在鸿蒙上返回与 Android / iOS 语义一致的字符串;
- 生成可编译、可运行、可验证的
Example/ohos宿主工程,在真机上跑通完整链路。
二、环境准备
环境搭建参考社区文档:Flutter OH 开发环境搭建,完成 Flutter OH SDK 安装、环境变量和 DevEco Studio 配置。
完成后,在宿主机终端执行以下命令,确认当前选中的是支持 OHOS 的 Flutter 工具链,并能发现目标设备:
flutter --version
flutter doctor -v
hdc list targets


工程使用的工具链和 SDK 配置如下:
| 项目 | 版本或配置 | 用途 |
|---|---|---|
| Flutter OHOS SDK | 3.47.7-ohos-1.0.0 | Flutter 编译与 OHOS 平台工具链 |
| Flutter 分支 | oh-3.47-dev | 对应开发分支 |
| Dart SDK | 3.13.3 | Dart 语言与包管理环境 |
| DevEco Studio | 26.0.0(26.0.0.851) | OHOS 应用工程开发套件 |
compatibleSdkVersion | 6.1.1(24) | 当前工程声明的最低兼容版本 |
targetSdkVersion | 6.1.1(24) | 应用面向的行为版本 |
| 测试设备 ROM | OpenHarmony-6.1.1.120(API 24) | 真机系统版本(TLR-AL00,nova 14) |
| 库版本 | 1.0.1 | pubspec.yaml 中的包版本 |
| 原生语言 | ArkTS | ohos/ 模块实现语言 |
| 插件产物 | HAR(ohos/) | 经 GeneratedPluginRegistrant.ets 自动注册 |
2.1 开发套件版本与工程中的 SDK 版本配置
6.1.1(24) 和工程配置涉及 OpenHarmony 版本号体系。在本文工程中,相关版本的含义与配置方式如下:
6.1.1(24)采用「版本(API)」写法,表示 OpenHarmony6.1.1版本、对应 API 24;compatibleSdkVersion与targetSdkVersion均为6.1.1(24):声明最低兼容 API 24,并以 API 24 为行为目标;- 本机安装的 DevEco Studio 自带 SDK 为
26.0.0(26),高于工程声明值,编译按本机 SDK 执行、安装兼容性按工程声明判断,两者并不冲突。

这里有一个本库实际踩过的坑:Example 工程最初沿用模板的 26.0.0,在 API 24 的真机上安装时报 code:9568297 older sdk version in the device——设备的 API 版本低于工程声明值,HAP 直接被拒装。本次适配把两个版本值统一下调到 6.1.1(24) 后安装成功,详见 9.4。
三、从源码仓库开始准备适配工程
3.1 将上游源码同步到 AtomGit
适配已有第三方库时,先从 Pub 包信息或项目 README 确认上游地址、包名、版本和许可证。同步仓库时保留源码、许可证和提交历史,方便后续跟踪上游更新。
在 AtomGit 网页的新建仓库流程中,找到从已有仓库导入的入口,填写上游 Git 地址,选择自己有写权限的组织或个人空间,设置目标仓库名称后执行导入。完成后检查目标分支、pubspec.yaml、LICENSE 和提交历史是否完整,并记录适配起点的提交号。如果上游本身托管在 AtomGit,也可以通过 Fork 获得自己的工作仓库。
本例的上游为 josercc/flutter_channel_json,包名 flutter_channel_json,pubspec.yaml 中的版本为 1.0.1(上游另有 v1.0.2 标签,但其指向的提交里 pubspec.yaml 版本仍是 1.0.1,适配 TAG 按包版本命名为 1.0.1-ohos-1.0.0-beta.1)。有一点必须如实说明:上游 LICENSE 文件的内容是占位文本(「暂时还没想好」),尚未声明具体开源协议,本次适配未擅自更改协议,分发时保留上游 LICENSE 原文即可。
flutter_channel_json 现可从 AtomGit 获取。下面使用 AtomGit 地址拉取代码;需要提交修改时,使用自己有写权限的仓库或 Fork。
3.2 将代码拉取到宿主机
在安装了 Flutter OH 和 DevEco Studio 的开发电脑上打开终端,进入准备存放项目的目录,执行:
git clone https://atomgit.com/oh-flutter/flutter_channel_json.git
cd flutter_channel_json
pwd
ls
git remote -v
git status --short --branch
git rev-parse HEAD
git clone 会创建 flutter_channel_json/ 目录;cd 后的位置就是下文所说的仓库根目录,这里应能看到 pubspec.yaml、lib/、ohos/ 和 Example/。Git 仓库名与 Dart 包名相同,都是 flutter_channel_json。
需要使用与本文相同的代码版本时,在没有未提交修改的仓库中切换到以下提交:
git switch --detach 029804c4f9205ecb21cbcc7bfc6a09fdc3437835
适配其他插件时,使用该插件对应版本的 tag 或 commit 作为分支起点。

图 1:在宿主机终端输入 AtomGit 仓库拉取命令。
3.3 在仓库根目录确认分支与版本标记
本库没有为 OHOS 适配单独开特性分支,适配提交直接落在 main 上,版本通过 TAG 标记。TAG 命名规则为 原库版本-ohos-版本号-beta.x,本库使用 1.0.1-ohos-1.0.0-beta.1。在 flutter_channel_json/ 根目录执行:
git branch --show-current
git tag --list
git log --oneline -3
git status --short --branch
git branch --show-current 用于确认当前所在分支,git tag --list 用于确认版本标记是否已推送。向接收仓库提交时按其对分支与 TAG 的要求执行。

图 2:在 flutter_channel_json 仓库根目录确认分支与 TAG。
3.4 补全 ohos 平台与 Example 宿主工程
与纯 Dart 包不同,本库上游已经按插件形式声明了 android 与 ios 平台,只是缺少 ohos。补全方式是在仓库根目录执行插件模板生成,让它同时产出 ohos/ 模块与 Example/ohos/ 宿主工程:
flutter create . --template=plugin --platforms=ohos
git status --short
git diff -- pubspec.yaml
--template=plugin匹配本库的插件身份,生成 HAR 模块骨架;--platforms=ohos指定只补全 OHOS 平台,不影响已有的android/、ios/实现;- 最后的
.表示在当前插件根目录内补全,不是另建一层目录。
生成后务必用 diff 检查:模板会连带注入与 OHOS 无关的内容,例如 federated 骨架(lib/*_method_channel.dart、lib/*_platform_interface.dart)、.kts 构建脚本、*.iml 等。本次适配保留了已有 API、其他平台注册项及依赖配置,只保留 ohos/ 与 Example/ohos/ 相关产物。不同 Flutter OH 版本生成的模板可能略有差异。
pubspec.yaml 的净变化只有 2 行——在 flutter: plugin: platforms 下追加 ohos 声明:
flutter:
plugin:
platforms:
android:
package: com.example.flutter_channel_json
pluginClass: FlutterChannelJsonPlugin
ios:
pluginClass: FlutterChannelJsonPlugin
ohos:
pluginClass: FlutterChannelJsonPlugin
pluginClass 必须与 ArkTS 侧 getUniqueClassName() 的返回值一致(都是 FlutterChannelJsonPlugin),否则插件注册表找不到类。

图 3:在仓库根目录输入 OHOS 平台补全命令。
3.5 适配后的项目目录
适配后的关键目录如下:
flutter_channel_json/
├── lib/
│ └── flutter_channel_json.dart
├── ohos/
│ ├── index.ets
│ ├── oh-package.json5
│ ├── build-profile.json5
│ ├── README.md
│ └── src/main/
│ ├── module.json5
│ └── ets/components/
│ ├── model/FlutterChannelJson.ets
│ └── plugin/FlutterChannelJsonPlugin.ets
├── Example/
│ ├── lib/
│ │ ├── main.dart
│ │ ├── home_page.dart
│ │ └── demo_log.dart
│ └── ohos/
│ ├── build-profile.json5
│ ├── oh-package.json5
│ └── entry/src/main/
│ ├── ets/entryability/EntryAbility.ets
│ ├── ets/plugins/GeneratedPluginRegistrant.ets
│ ├── module.json5
│ └── resources/
├── docs/ohos/
│ ├── REAL-DEVICE-TEST.md
│ ├── OHOS-ADAPTATION-BLOG.md
│ ├── images/
│ └── logs/
├── test/
├── README.OpenHarmony_CN.md
├── CHANGELOG.OpenHarmony.md
└── pubspec.yaml
项目根目录如下,其中包含 ohos/ 与 Example/ohos/,以及 OpenHarmony 中英文说明、变更记录、真机验证报告和适配实录:

图 4:适配后的 flutter_channel_json 项目根目录。
| 文件 | 主要职责 |
|---|---|
lib/flutter_channel_json.dart | 四字段契约的 Dart 实现,业务侧统一入口 |
ohos/src/main/ets/components/model/FlutterChannelJson.ets | ArkTS 数据模型,与 Dart 契约逐字段对齐 |
ohos/src/main/ets/components/plugin/FlutterChannelJsonPlugin.ets | 插件类:注册、方法分发、错误处理与 hilog 打点 |
ohos/index.ets | 导出插件类与数据模型,供宿主工程复用 |
Example/lib/home_page.dart | 示例页:预设、构造、往返校验、批量调用、契约表与日志 |
Example/ohos/build-profile.json5 | 声明 product、SDK 版本与签名配置 |
docs/ohos/REAL-DEVICE-TEST.md | 真机验证报告:环境、日志、截图、踩坑 |
README.OpenHarmony_CN.md / README.OpenHarmony.md | OpenHarmony 中英文说明 |
CHANGELOG.OpenHarmony.md | 鸿蒙化变更记录 |
四、Dart 实现分析
本库的全部 Dart 实现就是 lib/flutter_channel_json.dart 一个文件,上游同时存在 Kotlin 与 Swift 侧的同构实现。适配插件时,应保留其公开接口和其他平台实现,Dart 层本次零改动。
本例的实现全文如下:
import 'dart:convert';
/// 规范 Flutter 通道消息格式
class FlutterChannelJson<T> {
/// 状态码
final int code;
/// 消息
final String message;
/// 数据
final T? data;
/// 是否成功
final bool success;
const FlutterChannelJson({
required this.code,
required this.message,
this.data,
required this.success,
});
factory FlutterChannelJson.fromJson(Map<String, dynamic> json) {
return FlutterChannelJson<T>(
code: json['code'] as int,
message: json['message'] as String,
data: json['data'] as T?,
success: json['success'] as bool,
);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{
'code': code,
'message': message,
'data': data,
'success': success,
};
}
String toJsonString() {
return json.encode(toJson());
}
factory FlutterChannelJson.fromJsonString(String jsonString) {
return FlutterChannelJson.fromJson(json.decode(jsonString));
}
factory FlutterChannelJson.success(T data) {
return FlutterChannelJson<T>(
code: 200, message: '', data: data, success: true,
);
}
factory FlutterChannelJson.failure(String message) {
return FlutterChannelJson<T>(
code: 500, message: message, data: null, success: false,
);
}
}
4.1 跨端架构与调用时序
虽然类名叫 flutter_channel_json,但它的 Dart 层不创建任何 MethodChannel:库本身只负责「把消息编码成约定的 JSON、把收到的 JSON 解码回对象」。通道由业务或配套的 flutter_channel_event_bus 持有,原生侧按同一契约构造消息回传。四端的关系如下:
无论原生侧是哪一端,只要按契约封包,Dart 侧的解析代码就完全不用改——这正是这个库存在的意义。
4.1.1 一次完整回传的时序
4.2 数据契约模型
FlutterChannelJson<T> 的全部语义可以归纳为一张表:
| 场景 | code | message | data | success |
|---|---|---|---|---|
success(data) | 200 | 空串 | 传入值 | true |
failure(message) | 500 | 传入消息 | null | false |
| 默认构造 | 必填 | 必填 | 可空 | 必填 |
几个对业务有直接影响的细节:
fromJson用json['data'] as T?做类型收窄,data缺失时得到null,不会抛错;但code、message、success缺失或类型不符会抛TypeError;fromJsonString内部先走json.decode,非法 JSON 字符串抛FormatException,example 的「非法 JSON」按钮演示的正是这条路径;- 泛型
T只在 Dart 侧收窄,JSON 本身不带类型信息——原生侧封了什么类型,Dart 侧就要用对应的T去解。
4.3 公开 API
| 名称 | 描述 | 类型 |
|---|---|---|
FlutterChannelJson(...) | 默认构造,四字段显式指定 | 构造方法 |
FlutterChannelJson.fromJson | 根据字典初始化 | 工厂方法 |
FlutterChannelJson.fromJsonString | 根据 JSON 字符串初始化 | 工厂方法 |
FlutterChannelJson.success | 成功格式,code 固定 200 | 工厂方法 |
FlutterChannelJson.failure | 失败格式,code 固定 500 | 工厂方法 |
code / message / data / success | 四字段契约 | 属性 |
toJson / toJsonString | 序列化为字典 / JSON 字符串 | 方法 |
4.4 为什么原生侧只有一个 getPlatformVersion
这是上游的设计,不是适配遗漏。Dart 层没有实现任何通道调用,原生侧(Android / iOS)只保留了插件模板自带的 getPlatformVersion——它存在的意义是提供一条可验证的最小通路:业务接入后调用一次,能拿到版本串就说明通道两端已经接通。本次适配与 Android / iOS 保持一致:不新增、不删除任何方法,Dart 层保持零改动,原生侧同样只实现 getPlatformVersion。
五、ArkTS 平台实现
5.1 插件注册
pubspec.yaml 声明 ohos 平台后,Flutter 工具会在构建时为宿主工程生成注册代码。本库适配后的 Example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets 内容如下:
import { FlutterEngine, Log } from '@ohos/flutter_ohos';
import FlutterChannelJsonPlugin from 'flutter_channel_json';
export class GeneratedPluginRegistrant {
static registerWith(flutterEngine: FlutterEngine) {
try {
flutterEngine.getPlugins()?.add(new FlutterChannelJsonPlugin());
} catch (e) {
Log.e(TAG, "Tried to register plugins with FlutterEngine failed.");
Log.e(TAG, "Received exception while registering", e);
}
}
}
GeneratedPluginRegistrant.ets 是生成文件,通常不应手工编辑,下次构建可能覆盖它;注册是否生效可以直接看 import 语句里有没有本库条目。
5.2 数据模型:与 Dart 契约逐字段对齐
ohos/src/main/ets/components/model/FlutterChannelJson.ets 是本次适配的核心产出之一:
export type FlutterChannelJsonMap = Record<string, ESObject>;
export default class FlutterChannelJson<T> {
code: number;
message: string;
data: T | null;
success: boolean;
static success<T>(data: T): FlutterChannelJson<T> {
return new FlutterChannelJson<T>(200, '', data, true);
}
static failure<T>(message: string, code: number = 500): FlutterChannelJson<T> {
return new FlutterChannelJson<T>(code, message, null, false);
}
static fromJson<T>(json: FlutterChannelJsonMap): FlutterChannelJson<T> { /* ... */ }
static fromJsonString<T>(jsonString: string): FlutterChannelJson<T> { /* ... */ }
toJson(): FlutterChannelJsonMap { /* ... */ }
toJsonString(): string { return JSON.stringify(this.toJson()); }
}
几个刻意的对齐点:
| 细节 | Dart | ArkTS 实现 |
|---|---|---|
| 成功码 | 200,message 为空串 | 相同 |
| 失败码 | 500,data 为 null | 相同(failure 额外支持自定义 code,与 Kotlin 的 failure(message, code) 对齐) |
| 键名 | code/message/data/success | 完全相同,JSON.stringify 后可直接被 Dart 解析 |
data 为空 | null | null(fromJson 里把 undefined 归一成 null,避免 Dart 侧收到 undefined) |
模型经 ohos/index.ets 对外导出,宿主工程里的其它插件可以直接 FlutterChannelJson.success(data).toJsonString() 回传数据,而不必再手写一遍四字段字典。
5.3 插件类:注册、分发与全量错误处理
FlutterChannelJsonPlugin.ets 的职责是把通道接起来,并保证任何路径都有回音:
const CHANNEL_NAME: string = 'flutter_channel_json';
export default class FlutterChannelJsonPlugin implements FlutterPlugin, MethodCallHandler {
private channel: MethodChannel | null = null;
getUniqueClassName(): string {
return "FlutterChannelJsonPlugin";
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
try {
this.channel = new MethodChannel(binding.getBinaryMessenger(), CHANNEL_NAME);
this.channel.setMethodCallHandler(this);
hilog.info(DOMAIN, TAG, 'onAttachedToEngine success, channel: %{public}s', CHANNEL_NAME);
} catch (e) {
const error = e as BusinessError;
hilog.error(DOMAIN, TAG, 'onAttachedToEngine failed, code: %{public}d, message: %{public}s',
error.code, error.message);
}
}
onMethodCall(call: MethodCall, result: MethodResult): void {
try {
if (call.method == "getPlatformVersion") {
const version: string = this.getPlatformVersion();
hilog.info(DOMAIN, TAG, 'onMethodCall %{public}s -> %{public}s', call.method, version);
result.success(version);
} else {
result.notImplemented();
}
} catch (e) {
const error = e as BusinessError;
hilog.error(DOMAIN, TAG, 'onMethodCall %{public}s failed, code: %{public}d, message: %{public}s',
call.method, error.code, error.message);
result.error('FLUTTER_CHANNEL_JSON_ERROR', 'Failed to handle method call: ' + call.method,
error.message);
}
}
}
要点:
- 通道名
flutter_channel_json与 Android / iOS 完全一致; - 未实现方法走
result.notImplemented(),Dart 侧得到MissingPluginException,与 Android 的同名行为一致; - 注册、分发、异常三个环节都写 hilog(TAG
FlutterChannelJsonPlugin),只靠hdc shell hilog就能验证通道接线,不必依赖界面; - 任何异常都以
FLUTTER_CHANNEL_JSON_ERROR回传,避免「Dart 侧一直 await 到超时」这种最难查的问题。
5.4 getPlatformVersion:三级取值对齐两端语义
Android 返回 Build.VERSION.RELEASE,iOS 返回 UIDevice.current.systemVersion,两者都是「用户可见的系统版本」。鸿蒙上最先想到的 deviceInfo.osFullName(形如 OpenHarmony-6.1.1.120)在 HarmonyOS 设备上并不是用户看到的版本。最终实现是三级兜底:
private getPlatformVersion(): string {
const osName: string = deviceInfo.distributionOSName ? deviceInfo.distributionOSName : '';
const osVersion: string = deviceInfo.distributionOSVersion ? deviceInfo.distributionOSVersion : '';
if (osName.length > 0 && osVersion.length > 0) {
return osName + ' ' + osVersion; // HarmonyOS 6.1.0
}
if (deviceInfo.osFullName && deviceInfo.osFullName.length > 0) {
return deviceInfo.osFullName; // OpenHarmony-x.x.x.x
}
return 'OpenHarmony API ' + deviceInfo.sdkApiVersion;
}
取值优先级的理由:
distributionOSName + distributionOSVersion才是「发行版名 + 版本」,与 Android / iOS 的「平台名 + 版本」语义最贴近,真机实测输出HarmonyOS 6.1.0;- OpenHarmony 设备(无发行版信息)退到
osFullName,其本身已含平台名前缀,不再重复拼接; - 最后用
sdkApiVersion兜底,保证任何设备都有可读返回值,不会出现空串。
以上字段均不需要申请权限(@ohos.deviceInfo 中仅 UDID / ODID / 磁盘序列号等需要)。
5.5 权限
5.5.1 插件侧:HAR 无权限
ohos/src/main/module.json5 是 HAR 模块声明,没有 requestPermissions 配置段。插件读取的系统信息字段属于免权限接口。
5.5.2 应用 entry 的权限
Example/ohos/entry/src/main/module.json5 中只有模板自带的 ohos.permission.INTERNET:
"requestPermissions": [
{"name" : "ohos.permission.INTERNET"},
]
这一条是宿主工程模板自带的,与本库的功能无关,保留或删除都不影响运行。除此之外整个工程没有任何权限声明,也不涉及运行时授权。
六、补全交付文件并提交适配分支
6.1 除代码外还要补全哪些文件
代码适配完成后,还需要整理安装说明、接口文档、版本记录和开源信息。接收仓库有专用模板时,按其格式填写:
| 文件 | 应写清楚的内容 |
|---|---|
README.OpenSource | 上游名称、源码地址、适配版本或提交、版权及许可证信息;按仓库模板列出第三方依赖 |
README.md | 原项目说明、OHOS 支持入口、配套 Demo 和文档链接;保留上游信息 |
README.OpenHarmony_CN.md | 简介、安装方式、版本对应关系、环境约束、权限、接口表、示例、已验证范围和遗留问题 |
README.OpenHarmony.md | 与中文说明对应的英文文档 |
CHANGELOG.OpenHarmony.md | OHOS 新增能力、适配版本、兼容限制与测试范围 |
LICENSE / NOTICE | 保留上游 LICENSE 原文;上游未声明协议时不要擅自添加 |
example/README.md | 依赖方式、运行目录、签名、操作步骤与效果图 |
pubspec.yaml | 核对包名、版本、SDK 约束和依赖 |
本仓库已提供 README.OpenHarmony_CN.md、README.OpenHarmony.md、CHANGELOG.OpenHarmony.md、ohos/README.md 与 docs/ohos/ 文档集。发布前有三处需要核对统一:
- 两份 README 的下载地址目前写的是
atomgit.com/CPF-Flutter/fluttertpc_flutter_channel_json,与实际托管位置atomgit.com/oh-flutter/flutter_channel_json不一致,接入时以本文 7.2 的配置为准,或先统一文档地址; - README 的 TAG 表中分支列写的是
br_v1.0.1_ohos,实际分支为main,发布前统一; pubspec.yaml的 SDK 约束保持上游的>=2.16.1 <3.0.0未改动,本次在 flutter_ohos 工具链(Dart 3.13.3)下flutter pub get与flutter build hap --debug均正常通过;如在其他工具链遇到 SDK 上界报错,可参考同类适配的做法放宽上界。
6.2 提交前检查
提交前先完成第八节的 example 和真机测试,再从根目录检查改动:
git branch --show-current
git diff --check
git status --short
git diff --stat
git diff
检查 diff 中的依赖变化,移除本机路径及签名信息,并使文档中的版本和分支与提交内容一致。
签名凭据不入库: 本仓库的 Example/ohos/build-profile.json5 中 signingConfigs 保持为空数组,调试签名材料只保留在开发者本机(~/.ohos/config 下的自动签名材料与个人账号绑定,不适合进入公开仓库)。真机安装前需按 8.3 在 DevEco Studio 中生成自己的调试签名。
6.3 提交并推送到 AtomGit
文档和代码整理完成后,在根目录暂存并提交。文件名按项目实际情况调整:
git add lib pubspec.yaml ohos Example docs test
git add README.OpenHarmony_CN.md README.OpenHarmony.md
git add CHANGELOG.OpenHarmony.md
git diff --cached --check
git diff --cached --stat
git commit -m "feat: add OpenHarmony platform support"
git remote -v
git branch --show-current
git push -u origin main
git tag 1.0.1-ohos-1.0.0-beta.1
git push origin 1.0.1-ohos-1.0.0-beta.1
本例的适配已合并为一次提交 029804c,推送在 AtomGit 的 main 分支上,TAG 1.0.1-ohos-1.0.0-beta.1 已随提交推送(见图 2)。
推送后在 AtomGit 发起合并请求,说明上游来源和版本、适配范围(ArkTS 数据模型 + 唯一通道方法,Dart 层零改动)、依赖变化(仅 pubspec 新增 ohos 平台声明)、测试环境、操作结果、已知限制,并附 Demo 运行图。目标分支和评审流程以接收仓库要求为准。
七、使用根目录 Example 演示接入
仓库自带 Example/,可以直接用来调试和体验四字段契约在鸿蒙上的完整链路。
7.1 本地适配时使用路径依赖
当前 Example/pubspec.yaml 的依赖是:
dependencies:
flutter:
sdk: flutter
flutter_channel_json:
path: ../
../ 相对于 Example/pubspec.yaml 指向仓库根目录,修改根目录的库代码后可直接联调。
7.2 通过 AtomGit 引入库
业务应用通过 AtomGit 引入时,将 flutter_channel_json 的 path 配置替换为下面的 Git 依赖。这里固定到本文使用的 TAG:
dependencies:
flutter:
sdk: flutter
flutter_channel_json:
git:
url: https://atomgit.com/oh-flutter/flutter_channel_json.git
ref: 1.0.1-ohos-1.0.0-beta.1
使用自己的适配版本时,先推送分支,再将 url 改为对应仓库,ref 改为分支名。正式发布后可固定到 tag 或 commit。
从仓库根目录执行:
cd Example
flutter pub get
flutter pub deps
检查 Example/pubspec.lock 中 flutter_channel_json 的来源为 git,并核对 url、ref 和 resolved-ref。同时检查没有 dependency_overrides 或 pubspec_overrides.yaml 将其覆盖回本地依赖,确认应用使用的是 Git 依赖。
7.3 调用接口完成消息编解码
下面的片段展示 Dart 侧的核心用法,可直接用于业务页面:
import 'package:flutter_channel_json/flutter_channel_json.dart';
// 原生侧回传的 JSON 字符串
const raw = '{"code":200,"message":"","data":2,"success":true}';
final result = FlutterChannelJson<int>.fromJsonString(raw);
if (result.success) {
// 正常路径:code == 200,data 已按 T 收窄
print('data = ${result.data}');
} else {
// 业务失败:按 code / message 分支处理,而不是裸抛异常
print('failed(${result.code}): ${result.message}');
}
// 构造并序列化(Dart -> 原生的方向同理)
print(FlutterChannelJson<String>.success('ok').toJsonString());
// {"code":200,"message":"","data":"ok","success":true}
7.4 原生侧按契约回传
OpenHarmony 侧的插件代码用 ArkTS 模型构造同构消息,经业务持有的通道回传:
import FlutterChannelJson from 'flutter_channel_json';
// 成功格式
const successText: string = FlutterChannelJson.success<number>(2).toJsonString();
// 失败格式(code 可自定义,默认 500)
const failureText: string = FlutterChannelJson.failure<number>('message').toJsonString();
建议业务失败用 failure(message, code) 表达、由 Dart 侧按 code 分支处理,而不是在原生侧抛 result.error——四字段契约的价值就在于把「成功 / 失败」变成可解析的数据,而不是异常流。
八、验证、构建与鸿蒙设备运行效果
8.1 分别验证库与 example
本仓库包含单元测试 test/flutter_channel_json_test.dart,7 个用例分别覆盖默认构造、fromJson、fromJsonString、toJson、toJsonString、success 与 failure。从仓库根目录执行:
flutter pub get
flutter test
flutter analyze
cd Example
flutter pub get
flutter analyze
单元测试验证的是 Dart 层编解码行为,所有平台一致;通道链路与界面渲染还需要在鸿蒙设备上确认。
8.2 确认设备连接
hdc list targets
flutter devices
设备首次连接电脑时,需要在手机端确认调试授权。列表为空时,检查 USB 连接、调试模式和电脑授权。
8.3 配置签名
真机安装的 HAP 通常需要有效签名。仓库中的 signingConfigs 为空,推荐使用 DevEco Studio 为 entry 模块配置自动签名:
- 用 DevEco Studio 打开
Example/ohos,不是仓库根目录; - 等待工程 Sync 成功,确认 Project 视图中存在
entry模块; - 打开 File > Project Structure > Signing Configs;
- 勾选 Automatically generate signature;
- 确认设备、应用包名、证书和 Profile 匹配(调试 Profile 与
bundleName: com.example.flutter_channel_json_example强绑定); - 再回到终端执行构建或安装。
签名材料保存在本机,不要提交到仓库。SDK 自带的 OpenHarmony 默认调试证书链不被真机信任,无法通过自签名安装。
8.4 构建与安装
在 Example/ 目录执行 flutter run -d <device-id> 可以直接构建运行。仅构建 HAP 时:
flutter build hap --debug
Windows 环境下该命令可能因 DevEco Studio 26 的 ohpm.bat 批处理递归缺陷中断(见 9.11),可改用工程内等价的构建命令:
cd Example/ohos
hvigorw assembleHap -p product=default -p buildMode=debug --no-daemon
典型产物位于 Example/build/ohos/hap/entry-default-signed.hap。真机安装选择已签名产物:
hdc install -r entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_channel_json_example
本文验证使用的产物为 flutter build hap --debug 构建的已签名 HAP,工程声明的 compatibleSdkVersion / targetSdkVersion 为 6.1.1(24),与测试设备(API 24)一致。
通道接线还可以从 hilog 独立验证(TAG FlutterChannelJsonPlugin):
FlutterChannelJsonPlugin: onAttachedToEngine success, channel: flutter_channel_json
FlutterChannelJsonPlugin: onMethodCall getPlatformVersion -> HarmonyOS 6.1.0
8.5 在设备上测试契约与通道
example 页面按库的语义分支分区,逐项核对:
- 启动即自动探测一次
getPlatformVersion:运行环境卡显示 Dart 平台OpenHarmony / HarmonyOS、通道名flutter_channel_json、平台版本HarmonyOS 6.1.0,徽标变为「通道已连通」; - 点按快捷预设「成功 200」:表单与 JSON 输入框同步填充
code=200的四字段形态;依次点按「失败 500」「Map 数据」「List 数据」「bool 数据」,对照四种数据形态的序列化结果; - 在构造对象分区分别点按「默认构造」「success(data)」「failure(message)」,确认三种构造路径的输出;
- 在反序列化分区执行
fromJsonString、fromJson(Map),点按「往返校验」确认fromJsonString(toJsonString())与原始串一致;点按「非法 JSON」确认抛错路径有清晰报错而不是闪退; - 在序列化分区执行
toJson()/toJsonString(); - 在平台方法通道分区点按「获取平台版本」,确认返回
HarmonyOS 6.1.0;点按「连续调用 x5」,确认批量摘要为成功 5 次 / 失败 0 次并给出平均耗时;点按「调用未实现方法」,确认抛出MissingPluginException(notImplemented语义); - 对照四端字段契约表,确认 Dart / Kotlin / Swift / ArkTS 四列的字段类型与默认码(200 / 500)一致;
- 查看调用日志区:每次调用带时间戳,可用「全部 / 成功 / 异常」筛选。
本文已在真机完成上述全部 8 项并截图(见 8.6)。
8.6 鸿蒙设备运行效果
完成适配后,四字段契约与通道链路在 OpenHarmony 真机上与 Android / iOS 行为一致。
首屏的通道状态与运行环境:
| 通道状态与运行环境 |
|---|
徽标「通道已连通」,平台版本 HarmonyOS 6.1.0 由 getPlatformVersion 返回 |
快捷预设与构造对象、反序列化 / 序列化分区:
| 快捷预设与构造对象 | 反序列化与序列化 |
|---|---|
预设填充表单,success 开关与三种构造按钮齐全 | JSON 输入框与 fromJsonString / toJsonString() 按钮成对 |
平台方法通道与四端字段契约、未实现方法的异常语义:
| 连续调用 x5 与四端契约 | 未实现方法 |
|---|---|
| 成功 5 次 / 失败 0 次,总耗时 16 ms,平均 3.2 ms/次;契约表四列类型与默认码一致 | 结果预览区显示 MissingPluginException: No implementation found for method notImplementedMethod,与 Android 的 notImplemented 行为一致 |
结果预览与调用日志:
| 结果预览与调用日志 |
|---|
预览 getPlatformVersion -> HarmonyOS 6.1.0,日志按时间戳记录并支持筛选 |
getPlatformVersion 的返回值取决于设备系统(发行版信息),四字段编解码与通道行为则与设备硬件无关,在所有支持 Flutter 的 OpenHarmony 设备上一致。
九、FAQ:适配过程与使用问题
9.1 Missing SDK components
典型错误如下:
Missing SDK components. SDK path: ...,
missing components: toolchains,ets,js,native,previewer.
这个错误发生在 Hvigor 同步阶段。通常需要检查构建工具使用的 SDK 路径、组件是否完整,以及 Hvigor 与 SDK 的版本是否匹配。
处理顺序:
- 在 DevEco Studio SDK Manager 中确认对应 API 的组件已经下载完整;
- 检查 Flutter 和 DevEco Studio 使用的 SDK 路径是否一致;
- 避免误用不完整的 SDK 目录;
- 确认 SDK 根目录下存在
toolchains、ets、js、native、previewer; - 执行
flutter config --ohos-sdk <正确路径>; - 重新执行
flutter doctor -v和 DevEco Studio Sync。
因为 Sync 和 Compile 首先读取宿主机本地 SDK。手机 API 版本只在部署、安装和运行时参与兼容判断。即使完全不连接手机,本地 SDK 不完整时也会得到相同错误。
当前工程的 compatibleSdkVersion 是 6.1.1(24)(API 24),因此 API 24 设备在安装版本门槛上是满足的。
9.2 DevEco Studio 中看不到 entry 模块
可安装应用的 entry 模块位于 Example/ohos/entry。
请直接使用 DevEco Studio 打开:
flutter_channel_json/Example/ohos
如果仍看不到 entry,先解决 SDK Sync 错误,再检查 Example/ohos/build-profile.json5 的 modules 是否包含 ./entry。同步失败时,Project Structure 无法正确解析模块,签名界面也可能不显示 entry。
9.3 无法手动签名 / 签名材料从哪来
仓库中的 signingConfigs 刻意保持为空。调试签名材料按 8.3 的步骤在 DevEco Studio 中自动生成,与 bundleName(com.example.flutter_channel_json_example)强绑定。若业务改了自己的包名,需要重新生成对应的调试 Profile,否则安装时报签名与包名不匹配。
9.4 安装报 code:9568297 older sdk version in the device
本库实际踩过的坑:Example 工程最初沿用模板的 compatibleSdkVersion: 26.0.0,而真机是 API 24,安装直接被拒。处理方式是把 Example/ohos/build-profile.json5 中 product 的两个版本值统一下调:
"compatibleSdkVersion": "6.1.1(24)",
"targetSdkVersion": "6.1.1(24)",
判断依据是设备 param get const.ohos.apiversion 的返回值——工程声明的 API 版本不能高于目标设备的 API 版本。安装到更低 API 的设备时需同步下调。
9.5 安装报 no signature file 或 install version downgrade
真机安装的另外两个常见错误码:
| 报错 | 原因 | 处理 |
|---|---|---|
code:9568320 no signature file | 装了未签名 HAP | 使用 entry-default-signed.hap 安装 |
code:9568263 install version downgrade | 目标包名已被更高 versionCode 的应用占用 | 卸载旧应用、换包名或提升 versionCode |
9.6 调用未实现方法抛 MissingPluginException 是不是 bug
不是。ArkTS 侧对未注册的方法统一走 result.notImplemented(),Dart 侧对应的异常就是 MissingPluginException,与 Android / iOS 的行为一致。example 的「调用未实现方法」按钮演示这条路径(见 8.6 截图),业务侧应捕获它并提示「当前平台不支持该能力」,而不是当作崩溃处理。
9.7 修改了四字段契约,原生侧怎么同步
契约的四个键名散落在四端实现里。修改字段(增删改名)时,Dart、Kotlin、Swift、ArkTS 四份模型必须同步改,并同步更新单元测试与 example 的四端契约表。只改 Dart 侧的 fromJson 而不改 ArkTS 的 toJson,是最容易出现的静默失配——Dart 侧会因 TypeError 或 null 直接失败。建议以 example 的「往返校验」作为回归用例。
9.8 flutter create 不认识 ohos
先执行 flutter --version,确认使用的是 OHOS 版工具链,环境配置回到第二节的社区链接核对。注意 --template=plugin 只应对库根目录执行一次;对 Example/ 或其他目录重复执行会引入多余的模板文件,生成后务必 diff 检查并清理。
9.9 AtomGit 依赖提示找不到分支或无权限
先检查 URL 是否指向已同步的目标仓库(https://atomgit.com/oh-flutter/flutter_channel_json.git),再确认 TAG 1.0.1-ohos-1.0.0-beta.1 已推送。仓库 README 中目前的下载地址与 TAG 表分支名和实际不一致(见 6.1),接入时以本文 7.2 的配置为准,或先统一文档地址。私有仓库还需在本机配置 Git 认证。
9.10 改了本地库代码,Demo 为什么没变化
先检查 Example/pubspec.yaml:Git 依赖读取远程提交,不会自动读取本地库改动。本地联调切回 path: ../。Dart 文件变更用热重载/热重启即可生效;但 ohos/ 内 ArkTS 代码变更需要重新构建 HAP 并重新安装。
9.11 flutter build hap 卡在 ohpm install:BATCH RECURSION exceeds STACK limits
Windows 环境下最坑的一个问题,报错如下:
> hvigor start to execute ohpm install
****** B A T C H R E C U R S I O N exceeds STACK limits ******
Recursion Count=242, Stack Usage=90 percent
> hvigor ERROR: 00306053 Specification Limit Violation
Error Message: ohpm install failed.
排查路径:
- 手动
ohpm install完全正常 → 排除网络与依赖问题; bash里直接hvigorw assembleHap正常 → 说明与 flutter 的调用链有关;- 读
hvigorw.js发现它无条件把process.env.ohpmBin覆盖成 DevEco 自带的ohpm.bat;ohpm.bat的首参只要包含ohpm就会进逐字符递归; - 对比
PATH:Git Bash 的usr/bin里有一个无扩展名的ohpmshell 脚本,命令解析走了脚本分支,参数被整体当成%1,递归层数正好等于整条命令行长度。
解决:构建时使用不含 Git usr/bin 的 PATH(保留 Git/cmd 供 flutter 找 git),或改用 8.4 的 hvigorw assembleHap 命令。
9.12 spawn java ENOENT
越过 ohpm 之后,打包阶段(PackageHap)报:
> hvigor ERROR: Failed :entry:default@PackageHap...
> hvigor ERROR: Error Code: 00308018 Unknown Error
spawn java ENOENT
PackageHap 用 java 跑打包工具,环境里没有 java 就报这个「Unknown Error」。补 DevEco 自带 JBR(<DevEco Studio>/jbr)或设置 JAVA_HOME 即可,与工程代码无关。
相关链接
更多推荐

所有评论(0)