Flutter for OpenHarmony 实战:三方库 airplane_mode_checker 的鸿蒙化适配指南
环境搭建指引:https://atomgit.com/CPF-Flutter/flutter_samples/blob/master/ohos/getting-started/flutter-oh-env-setup.md
airplane_mode_checker 干两件事:读一次飞行模式,以及持续监听飞行模式的变化。它的 3.3.0 版支持 Android、iOS,没有 OpenHarmony。
它在鸿蒙上的适配形态很有代表性:安卓用 BroadcastReceiver 监听 ACTION_AIRPLANE_MODE_CHANGED,iOS 用 NSNotification,而鸿蒙对应的是系统公共事件 usual.event.AIRPLANE_MODE——从"广播"换到"公共事件",订阅/退订的时机、初值怎么给、失败怎么回落,都得一处处对齐。另外还有一个"坑得很真实"的点:读飞行模式的那个设置项已经从 API 21 起废弃,但替代品是系统/企业应用接口,普通三方应用根本用不了。
适配对象:上游 airplane_mode_checker 3.3.0(MIT);适配产物 TAG 3.3.0-ohos-1.0.0-beta.1。
一、这个库要解决什么
1.1 上游 API
// 平台版本
final String? version = await AirplaneModeChecker.instance.getPlatformVersion();
// 读一次:AirplaneModeStatus.on / .off
final AirplaneModeStatus status =
await AirplaneModeChecker.instance.checkAirplaneMode();
// 监听变化(流的第一帧就是当前值)
AirplaneModeChecker.instance.listenAirplaneMode().listen((AirplaneModeStatus s) {
print(s == AirplaneModeStatus.on ? '飞行模式已开' : '飞行模式已关');
});
1.2 契约:一条方法通道 + 一条事件通道
class MethodChannelAirplaneModeChecker extends AirplaneModeCheckerPlatform {
final methodChannel = const MethodChannel('airplane_mode_checker');
final eventChannel = const EventChannel('airplane_mode_checker_stream');
Future<String?> getPlatformVersion() async =>
await methodChannel.invokeMethod<String>('getPlatformVersion');
Future<String?> checkAirplaneMode({String defaultValue = 'OFF'}) async =>
await methodChannel.invokeMethod<String>('checkAirplaneMode',
<String, String>{'defaultValue': defaultValue});
Stream<String> listenAirplaneMode({String defaultValue = 'OFF'}) {
return eventChannel
.receiveBroadcastStream(<String, String>{'defaultValue': defaultValue})
.map((event) => event as String);
}
}
两个细节决定了适配的形状:
- 两个方法都会带一个
defaultValue(默认'OFF'),上游的语义是"拿不到就按这个值算"; - 事件通道的入参也带
defaultValue:receiveBroadcastStream({'defaultValue': 'OFF'})会把参数传给原生侧的onListen。
Dart 层没有任何平台门,pubspec.yaml 只声明了 android / ios,所以鸿蒙侧接住这两条通道即可。
1.3 基线
node .agents/tools/tree-diff.mjs _probe/cand13/airplane_mode_checker _probe/amc_work
# 相同: 92 内容不同: 0 仅 B 有: .github / .gitignore / .metadata 等工程文件
上游 master(ce46686)与 pub.dev 上的 3.3.0 完全一致。
二、选库:四道筛 + 在线查重
| 筛子 | 检查 | 结果 |
|---|---|---|
| ① pub.dev 平台列表 | 是否已含 ohos | [android, ios] → 需要适配 |
| ② 兄弟包 | <lib>_ohos / pub.dev 上 airplane_mode_checker_ohos | 都没有 |
| ③ Dart 平台门 | Platform.is* / defaultTargetPlatform 分支 | 无 |
| ④ 依赖体检 | dep-ohos-check.mjs airplane_mode_checker | deps ok: plugin_platform_interface(pure) |
在线查重(四组织全量快照 831 个仓库精确匹配):
---- airplane_mode_checker
干净。
三、六步适配流程
atomgit.mjs create oh-flutter airplane_mode_checker "…"建仓;- 克隆:
git clone https://gh-proxy.com/https://github.com/14h4i/airplane_mode_checker.git _probe/amc_work; - 建分支
feat/ohos_airplane_mode_checker_3.3.0,跑flutter create -t plugin --platforms ohos .; - 写
ohos/src/main/ets/components/plugin/AirplaneModeCheckerPlugin.ets; - 补三份 OpenHarmony 文档 + 根 README 说明 + 示例改自检台;
- 推送分支/main 并打 TAG,提交前清空
signingConfigs。

四、代码写在哪个文件
ohos/src/main/ets/components/plugin/AirplaneModeCheckerPlugin.ets # 本篇唯一新增的实现文件
4.1 安卓广播 → 鸿蒙系统公共事件
const AIRPLANE_MODE_EVENT: string = 'usual.event.AIRPLANE_MODE'; // 常量名 COMMON_EVENT_AIRPLANE_MODE_CHANGED
const subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {
events: [AIRPLANE_MODE_EVENT],
};
const subscriber = commonEventManager.createSubscriberSync(subscribeInfo);
commonEventManager.subscribe(subscriber, (err, data) => {
if (err) { Log.e(TAG, `common event error code=${err.code}`); return; }
const mode: string = this.readAirplaneMode(defaultValue); // 事件到了就重读真实值
Log.i(TAG, `common event ${data?.event} -> ${mode}`);
events.success(mode);
});
退订对应 commonEventManager.unsubscribe(subscriber),在 onCancel 里调用——不留订阅这点和上游一样。
4.2 与上游对齐的两处语义
onListen先推一次当前值:上游安卓在onListen里就调checkInitialAirplaneMode(),鸿蒙侧同样在订阅完成后立刻推一帧:
// 上游 onListen 里也是"注册完订阅立刻推一次初值"
const initial: string = this.readAirplaneMode(defaultValue);
Log.i(TAG, `stream listened, initial value = ${initial}`);
events.success(initial);
- 读不到就用调用方给的
defaultValue:checkAirplaneMode和事件通道都遵守这条。
4.3 读值需要 Context:插件要实现 AbilityAware
onAttachedToAbility(binding: AbilityPluginBinding): void {
this.ability = binding.getAbility();
}
private readAirplaneMode(fallback: string): string {
const ability = this.ability;
if (ability === null) { return fallback; } // 拿不到 Ability 也要按上游语义回落
try {
const raw: string = settings.getValueSync(ability.context, SETTING_KEY, '1');
return raw === '1' ? MODE_ON : MODE_OFF;
} catch (error) {
return fallback;
}
}
4.4 AIRPLANE_MODE_STATUS 已废弃,但三方应用没有替代品
SDK 里 settings.general.AIRPLANE_MODE_STATUS 自 API 21 起 deprecated,替代者是 @ohos.enterprise.deviceSettings / @ohos.enterprise.restrictions——系统应用/企业应用接口,普通三方应用无权调用。所以三方应用读飞行模式目前仍只能用它;本实现继续用,并在读失败时回落 defaultValue。
事件侧没有这个问题:
usual.event.AIRPLANE_MODE是普通公共事件,订阅不需要特殊权限。
4.5 这次 flutter create 生成的模板垃圾
android/build.gradle.kts、android/settings.gradle.kts、example/android/**.gradle.kts、example/integration_test/、example/ios/Runner/SceneDelegate.swift、ios/airplane_mode_checker/Sources/.../PrivacyInfo.xcprivacy 等——删之前先 git ls-files <目录>:本库上游 iOS 用的是复数 Sources/,只删模板新增的那个文件,别整目录删(第 11 篇就因为整目录删把上游被跟踪的文件一起删了)。
五、真机(模拟器)验证
示例改成了自检台:三个按钮(查平台版本 / 查飞行模式 / 订阅-取消订阅),界面记录事件次数与最近事件,日志前缀 [AMC-CHECK],原生日志按 AirplaneModeCheckerPlugin 过滤。
| 项 | 值 |
|---|---|
| 设备 | Pura 90 模拟器,HarmonyOS 6.1.1(24) / API 24,ohos-x64 |
| 构建 | flutter build hap --debug --target-platform ohos-x64 |
| 操作 | 设备侧结果 |
|---|---|
getPlatformVersion() | getPlatformVersion -> OpenHarmony OpenHarmony-6.1.1.125,Dart 侧拿到同一字符串 |
checkAirplaneMode() | checkAirplaneMode -> ON (fallback=OFF) —— 该模拟器当前飞行模式为开,settings.getValueSync 返回 1 并按 "ON" 上报 |
| 订阅后立刻收到初值 | stream listened, initial value = ON;界面"事件通道"由"未订阅"变为"订阅中(已收 1 次)" |
| 打开/关闭飞行模式后收到事件 | 未验证(原因见下) |
| 取消订阅后不再收事件 | 未验证(同上) |
为什么"变化事件"没验到(如实写明):这台模拟器的设置首页里没有可直接点到的飞行模式开关(emulator 那个 SIM 入口进去是空页/关于本机),设备上没有 settings 命令行工具(/bin/sh: settings: inaccessible or not found),而 hdc shell 的身份(uid=2000)没有权限修改系统设置。也就是说"制造一次飞行模式变化"这件事在模拟器上做不到——不是代码没跑通:事件路径与已通过的初值路径共用同一个 readAirplaneMode() + events.success(),只有触发源不同。



验证环境说明:本轮原计划用 API 26 的
Pura X View(HarmonyOS 7.0.0 Beta2),但该镜像在本机反复崩溃(宿主侧堆损坏,存活 20–60 秒),因此完整验证在 API 24 的Pura 90上完成。API 26 上已确认插件注册与 ability 绑定正常(airplane_mode_checker channels registered/ability attached),按钮点击坐标需按 API 26 布局重测。
六、编译与构建踩坑
6.1 示例依赖与模板用例会拦住构建
fluttertoast:上游示例用它弹提示,鸿蒙上无实现 → 从示例移除,改成"界面 +[AMC-CHECK]日志";integration_test:SDK 包在鸿蒙侧没有对应模块,留在dev_dependencies会让flutter build hap报AdaptorError 00303231: The srcPath is not a relative path: …/packages/integration_test/ohos;example/test/widget_test.dart:断言上游那套MyApp界面(find.text('Check Airplane Mode')),与鸿蒙自检台不匹配 → 删除。插件自身的 13 个单测保留,flutter test→All tests passed!。
6.2 示例改写后的自检顺序
flutter analyze # No issues found!
flutter test # 13 个用例全通过
flutter build hap --debug --target-platform ohos-x64
七、已知限制
- 读值接口已废弃:
settings.general.AIRPLANE_MODE_STATUS自 API 21 起 deprecated,三方应用无可用替代; - 不提供"设置飞行模式":本库 Dart API 只有读与监听,实现里也不调用需要系统权限的写接口;
- "变化事件"未在设备上验到(原因见第五节),事件路径与初值路径同源;
- 示例移除了
fluttertoast/integration_test,并删除了断言上游界面的 widget 测试。
八、常见问题
Q1:为什么鸿蒙要用"公共事件"监听飞行模式?
鸿蒙没有安卓那种可注册的广播;系统状态变化通过 SAMgr 的公共事件发布,usual.event.AIRPLANE_MODE 就是飞行模式开关变化对应的那一个(SDK 里常量名是 COMMON_EVENT_AIRPLANE_MODE_CHANGED)。
Q2:为什么插件需要 UIAbility?
settings.getValueSync(context, …) 需要 Context,插件默认只有 BinaryMessenger。所以实现 AbilityAware;拿不到 Ability 时也遵守上游语义回落 defaultValue,不抛异常。
Q3:settings.getValueSync 的第三个参数是什么?
默认值。实现里传 '1',所以只有明确读到 '0' 时才判定为关闭——这样"读不到"不会被误判成"飞行模式关"。
Q4:事件到了为什么还要重新读一次设置项?
公共事件只告诉你"变了",不保证带着新值。重新读一次 AIRPLANE_MODE_STATUS 才能拿到权威状态;这也和安卓实现一致(onReceive 里再查一次 Settings.Global.AIRPLANE_MODE_ON)。
Q5:订阅重复调用会怎样?
实现里对重复 onListen 做了保护:已经订阅时只补推一次当前值,不再重复 subscribe,避免事件被推多次、也避免退订时残留。
Q6:hdc shell 能不能直接改飞行模式来做验证?
不能。设备上没有 settings 命令行工具,且 hdc shell 的身份没有写系统设置的权限。要造变化只能在系统设置 UI 里点开关(本机模拟器恰好没有这个入口)。
Q7:平台版本返回什么?
OpenHarmony <deviceInfo.osFullName>。实测在 Pura 90 上是 OpenHarmony OpenHarmony-6.1.1.125。
Q8:需要声明权限吗?
读取与订阅都不需要权限声明。
九、本篇用到的库
| 项 | 值 |
|---|---|
| 适配仓库 | https://atomgit.com/oh-flutter/airplane_mode_checker |
| 上游仓库 | https://github.com/14h4i/airplane_mode_checker |
| 上游版本 | 3.3.0(MIT,master ce46686 与发布版逐文件一致) |
| 适配 TAG | 3.3.0-ohos-1.0.0-beta.1 |
| 适配分支 | feat/ohos_airplane_mode_checker_3.3.0 |
| 通道 | 方法通道 airplane_mode_checker、事件通道 airplane_mode_checker_stream |
| 鸿蒙侧依赖 | @kit.BasicServicesKit(settings / commonEventManager / deviceInfo)、@kit.AbilityKit |
dependencies:
airplane_mode_checker:
git:
url: https://atomgit.com/oh-flutter/airplane_mode_checker.git
ref: 3.3.0-ohos-1.0.0-beta.1
验证环境
| 项 | 值 |
|---|---|
| Flutter for OpenHarmony SDK | 3.44.9+ohos-0.0.1-canary1(Dart 3.12.2) |
| DevEco Studio | 26.0.0.621 |
| 设备 | Pura 90 模拟器,HarmonyOS 6.1.1(24) / API 24,ohos-x64 |
| 构建产物 | example/build/ohos/hap/entry-default-signed.hap |
复现命令
$env:PUB_CACHE = "E:\pub-cache"
cd _probe/amc_work/example/ohos
devecocli signature generate # 首次需要;提交前清空 signingConfigs
cd ..
flutter build hap --debug --target-platform ohos-x64
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.u14h4i.airplane_mode_checker_example
# 依次点:查平台版本 → 查飞行模式 → 订阅/取消订阅
hdc shell snapshot_display -f /data/local/tmp/amc.jpeg
hdc file recv /data/local/tmp/amc.jpeg .
hdc shell hilog -x | Select-String "AirplaneModeCheckerPlugin"
欢迎加入 CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐




所有评论(0)