Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 app_badge_plus
Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 app_badge_plus
应用角标是提升回访率最直接的手段:未读消息数、待办数、红点。但这件事在安卓上是出了名的碎片化——上游 app_badge_plus 为此写了一整套启动器适配层,impl/ 目录下按 Apex / Asus / Huawei / Hihonor / MiUI / OPPO / Samsung / Sony / Vivo 等近 20 个厂商分别处理。
鸿蒙这边反而简单:只有一个系统接口,不需要按厂商分支。本文以 app_badge_plus 1.3.5 为例,记录我如何把这套碎片化的逻辑收敛成一次 setBadgeNumber 调用,并在 HarmonyOS API 26 真机上验证到桌面图标真实显示红色角标。
适配仓库: oh-flutter/app_badge_plus
适配分支: feat/ohos_app_badge_plus_1.3.5
适配 TAG: 1.3.5-ohos-1.0.0-beta.1
一、先看最终效果

图 1:HarmonyOS API 26 真机启动自检台。顶部绿色显示 isSupported: true(可显示角标),下方是 +1 / -1 / 清零 / 设 5 / 设 120(99+)/ 重新查 isSupported 六个按钮。

图 2:点击「设 5」后,界面显示「界面记录的角标值:5」「最近一次调用:updateBadge(5) → 成功」,日志区同时打出 updateBadge(5) → 成功。

图 3:按 Home 键回到桌面,应用图标 app_badge_pl... 右上角显示红色角标「5」,与微信的 99+、QQ 的 99 并排显示。
| 验证点 | 实测结果 | 证据 |
|---|---|---|
| Flutter OH 3.47 工程构建 | 通过 | entry-default-signed.hap |
| 安装、启动(真机) | 通过 | 图 1 |
isSupported() | 返回 true | 图 1 |
updateBadge(5) | 调用成功 | 图 2 |
| 桌面图标角标 | 真实显示红色「5」 | 图 3 |
二、这个库原来是怎么工作的
app_badge_plus 的通道面只有两个方法,但安卓侧的实现量很大:
Dart AppBadgePlus
-> MethodChannel('app_badge_plus')
updateBadge({count}) // count == 0 表示清除
isSupported() // 当前设备/桌面是否支持角标
Android: 近 20 个启动器各自的私有广播或 ContentProvider
iOS/macOS: UIApplication.applicationIconBadgeNumber / NSApp.dockTile.badgeLabel
碎片化的根源在于:安卓没有统一的角标 API,每家启动器各搞一套。上游的做法是逐个厂商适配——这也是这个库存在的主要价值。
而鸿蒙不需要这一层。 @ohos.notificationManager 提供了统一的 setBadgeNumber,所有设备走同一条路径。
三、实际环境
Flutter 鸿蒙环境搭建不在本文重复展开,直接参考 Flutter OH 环境搭建指南。
| 组件 | 版本 |
|---|---|
| Flutter OH | 3.47.4-ohos-1.0.4 |
| Dart | 3.13.3 |
| DevEco Studio | 26.0.0 Release |
| HarmonyOS SDK | API 26 |
| 测试设备 | 华为真机 6UMBB26319007180,HarmonyOS 7.0.0.105(API 26) |
| 目标库 | app_badge_plus 1.3.5 |
flutter --version
hdc list targets
四、确认上游版本并同步到 AtomGit
上游包地址是 https://github.com/windows7lake/app_badge_plus,版本 1.3.5,使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现。
git clone https://atomgit.com/oh-flutter/app_badge_plus.git
cd app_badge_plus
git switch feat/ohos_app_badge_plus_1.3.5
git log --oneline -5
五、创建适配分支并检查工程结构
git switch -c feat/ohos_app_badge_plus_1.3.5
关键目录如下:
app_badge_plus/
├── lib/
│ └── app_badge_plus_method_channel.dart # 上游通道定义,一行未改
├── android/ # 上游的近 20 个启动器适配层,保持原样
├── ohos/
│ ├── index.ets # OHOS 插件导出入口
│ └── src/main/ets/components/plugin/
│ └── AppBadgePlusPlugin.ets # 通道实现
├── example/lib/main.dart # 真机验证自检台
├── example/ohos/ # Flutter OH 宿主工程
├── pubspec.yaml # 声明 ohos 平台
└── README.OpenHarmony_CN.md # 鸿蒙适配说明
注意 android/ 目录原封不动保留——鸿蒙适配只新增一个平台实现,不改动其他平台任何一行。
六、适配过程:一个接口取代 20 个分支
6.1 通道与方法名
const METHOD_CHANNEL: string = 'app_badge_plus';
const METHOD_UPDATE_BADGE: string = 'updateBadge';
const METHOD_IS_SUPPORTED: string = 'isSupported';
6.2 设置角标:setBadgeNumber
import { notificationManager } from '@kit.NotificationKit';
case 'updateBadge':
const count = call.argument('count') as number;
notificationManager.setBadgeNumber(count ?? 0)
.then(() => result.success(null))
.catch((e: BusinessError) => result.error(`${e.code}`, e.message, null));
return;
count == 0 表示清除,> 99 系统显示为 99+——这两个语义都由系统统一处理,实现里不需要特判。
6.3 isSupported 不是无脑 true
这一点我觉得是这个库适配里最有意思的地方。鸿蒙的角标是通知角标:如果用户在系统里把通知总开关关掉,角标就不会显示。所以 isSupported() 必须如实反映这个前提:
private async isSupported(): Promise<boolean> {
const enabled = await notificationManager.isNotificationEnabled();
if (!enabled) {
return false; // 通知总开关关了,角标必然不显示
}
const setting = await notificationManager.getNotificationSetting();
return setting.badgeNumberEnabled === true;
}
这里刻意没有返回无条件 true。 理由很直接:如果通知被关掉却告诉调用方「支持角标」,调用方就会以为设置成功了,但用户桌面上什么都不会出现——这是最糟糕的那种「假成功」。
6.4 双次回读:对抗异步落库
系统的角标状态是异步落库的,设置完立刻读可能拿到旧值。所以实现里做了两次回读:
const READBACK_SETTLE_MS: number = 300;
private async updateBadge(count: number): Promise<void> {
await notificationManager.setBadgeNumber(count);
// 立刻回读一次
await notificationManager.getBadgeNumber();
// 等状态落库,再回读一次拿"最终值"
await new Promise<void>((resolve) => setTimeout(resolve, READBACK_SETTLE_MS));
await notificationManager.getBadgeNumber();
}
这不是多余的操作——第一次回读能发现「参数根本没生效」这类立刻失败,第二次回读拿到的是落库后的真实值。两次结果都会写进 hilog,排查时能看出进度。
| 方法 | OpenHarmony 实现 |
|---|---|
updateBadge(count) | notificationManager.setBadgeNumber(count) + 两次回读 |
isSupported() | isNotificationEnabled() && getNotificationSetting().badgeNumberEnabled |
七、构建配置与签名
同批库共有的两个坑。
7.1 compatibleSdkVersion 必须写 "26.0.0"
00306004 Specification Limit Violation
The project's compatibleSdkVersion: 18 cannot be lower than
the minimum compatible version 24 required by the dependencies: @ohos/flutter_ohos.
正确写法是不带括号的 "26.0.0"。
7.2 签名证书绑定了 bundleName
00303074 Configuration Error
The bundleName in app.json5/hvigorfile.ts does not match the bundleName
in the generated SigningConfigs.
本库示例原本是 me.liolin.app_badge_plus_example,改成证书对应的 com.example.flutter_oh_demo 后通过。
八、通过 AtomGit 引入适配版
dependencies:
app_badge_plus:
git:
url: https://atomgit.com/oh-flutter/app_badge_plus.git
ref: 1.3.5-ohos-1.0.0-beta.1
flutter pub get
本库不需要任何权限声明——setBadgeNumber 从 API 10 起对普通应用开放,不需要额外权限。
九、构建、安装和真机验证
cd example
flutter pub get
flutter analyze
flutter build hap --debug
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo
真机操作顺序与结果:
| 操作 | 界面显示 | hilog |
|---|---|---|
| 启动 | isSupported: true(可显示角标) | isSupported() → true |
| 点「设 5」 | 界面记录的角标值:5最近一次调用:updateBadge(5) → 成功 | updateBadge(5) → 成功 |
| 按 Home 回桌面 | 图标右上角红色角标 5 | — |
第三张截图(图 3)是最有说服力的证据:我们的应用图标和微信(99+)、QQ(99)、抖音(10)排在一起,右上角清清楚楚是红色「5」。这证明 setBadgeNumber 真的改到了桌面图标,而不是只在应用内部记了个数字。
十、适配结果和当前边界
两个方法全部接通,Dart 层一行未改,安卓侧近 20 个启动器分支原样保留。
已知边界:
- 角标依赖通知总开关:鸿蒙的角标是通知角标,用户在系统设置里关掉通知总开关后,
isSupported()会返回false,此时updateBadge仍会记录数字但桌面不显示——这是平台语义,不是实现缺陷; - 上限 99+ 由系统处理:传 120 时系统显示
99+,实现里不做截断; - 桌面布局影响可见性:如果应用被放进文件夹或者被其他角标应用遮挡,截图时可能看不到,但角标本身已设置成功;
isSupported依赖系统查询:isNotificationEnabled或getNotificationSetting查询失败时,实现选择返回false(宁可说「不支持」也不说「支持但实际不显示」)。
十一、FAQ
Q1:为什么鸿蒙不需要像安卓那样适配 20 个厂商?
- 现象: 上游安卓侧有一整套
impl/启动器适配层。 - 解决方法: 鸿蒙
@ohos.notificationManager提供统一的setBadgeNumber,所有设备走同一条路径,不需要分支。 - 验证结果: 一次调用成功,桌面真实显示角标。
Q2:isSupported 为什么可能返回 false?
- 现象: 设备明明支持角标,但
isSupported()返回了false。 - 解决方法: 检查系统设置里的通知总开关,以及
badgeNumberEnabled是否开启。鸿蒙角标依附于通知,通知关闭则角标不显示。 - 验证结果: 本机通知开启,返回
true(图 1)。
Q3:设置完角标为什么要回读两次?
- 现象: 设置后立刻读可能拿到旧值。
- 解决方法: 立刻回读一次抓「即时失败」,等 300ms 状态落库后再回读一次拿最终值。
- 验证结果: 两次回读都记进 hilog,
updateBadge(5)最终值正确。
Q4:传 0 是清除还是显示 0?
- 现象: 不确定
updateBadge(0)的行为。 - 解决方法: 鸿蒙约定
0表示清除角标,不是显示「0」。 - 验证结果: 界面上的「清零」按钮走的就是这条路径。
十二、总结
app_badge_plus 1.3.5 已接入鸿蒙:用统一的 notificationManager.setBadgeNumber 取代了安卓的 20 个启动器分支,isSupported() 如实反映「通知开关 + badgeNumberEnabled」两个前提而不是无脑返回 true。真机上桌面图标真实显示红色角标「5」,与微信、QQ 的角标并排。
这个库最值得记的一条经验是:鸿蒙在很多能力上比安卓「统一」得多,把碎片化的兼容代码整段砍掉,反而是最正确的适配。app_badge_plus 上游 20 个启动器分支在鸿蒙侧一行都用不上,实现只需要一个系统接口——这种情况下,少写代码就是好适配。
十三、参考资料和仓库地址
- 适配仓库主页
- 适配分支
- 适配 TAG
- ArkTS 通道实现
- 鸿蒙适配说明
- pub.dev 包页面
- pub.dev 1.3.5 版本页
- 上游仓库
- Flutter 官方插件与平台通道文档
- Flutter OH 鸿蒙 SDK 仓库
- HarmonyOS 开发者官方文档
欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter
更多推荐

所有评论(0)