动态应用图标是一个“看起来很小、实现差异极大”的能力。Android 侧要靠 activity-alias 预注册多套图标并切换 alias 的启用状态,还得绕开部分厂商的黑名单;iOS 侧走 setAlternateIconName。flutter_dynamic_icon_plus 把两套机制封装成一致的方法。把它带到鸿蒙时,问题是:鸿蒙侧既没有等价的图标切换系统 API,也没有角标数字接口,这种情况下"适配"应该做什么?

本文以 flutter_dynamic_icon_plus 1.4.1 为例,记录我如何保留完整的方法通道、在鸿蒙端如实返回能力边界,并在 HarmonyOS API 26 模拟器上完成构建、安装、启动和方法调用验证。上机结果没有出现“切换成功”的假象:所有查询接口返回明确的否定值,所有切换接口保持安全无操作。

适配仓库: oh-flutter/flutter_dynamic_icon_plus

适配分支: feat/ohos_flutter_dynamic_icon_plus_1.4.1

适配 TAG: 1.4.1-ohos.1

一、先看最终效果

图 1:HarmonyOS API 26 模拟器启动 Demo。页面显示 flutter_dynamic_icon_plus 1.4.1,状态为等待操作。

图 2:点击“查询图标支持情况”后的真实结果。supportsAlternateIcons 返回 false,当前图标名为 null。

验证点实测结果证据
Flutter OH 3.47 工程构建通过entry-default-signed.hap
签名、安装和启动通过图 1
supportsAlternateIcons返回 false图 2
getAlternateIconName返回 null图 2
setAlternateIconName安全无操作图 3
角标读写返回 0 / 安全无操作图 4
鸿蒙图标切换能力尚未接入适配边界

继续在模拟器上点击其余两个按钮,确认切换和角标入口也都保持明确的语义:

图 3:调用 setAlternateIconName 后返回完成,但不会伪造图标已切换的状态。

图 4:调用 applicationIconBadgeNumber 后返回 0,与“该平台没有角标机制”一致。

二、这个库原来是怎么工作的

上游包的公共入口是 FlutterDynamicIconPlus 的五个静态方法。Dart 层通过名为 flutter_dynamic_icon_plus 的 MethodChannel 把调用传给原生端。

Dart FlutterDynamicIconPlus
  -> MethodChannel('flutter_dynamic_icon_plus')
  -> Android activity-alias / iOS setAlternateIconName
  -> 图标名、支持状态或角标数字

Android 的实现需要宿主应用预先在 AndroidManifest.xml 里声明多个 activity-alias,切换图标本质上是启用或禁用这些 alias,所以包还提供了 blacklistBrands、blacklistManufactures、blacklistModels 参数来绕开已知不稳定的厂商机型。这些参数在鸿蒙端没有对应含义,但为了接口签名兼容,这次适配保留参数原样接收。

值得单独说明的是 iOS 角标:applicationIconBadgeNumber 是 iOS 独有的概念,Android 压根没有系统级角标接口。也就是说这条库本身在 Android 上就只实现了方法签名的一部分。鸿蒙端沿用了同样的策略:保留方法,如实返回边界。

三、实际环境

Flutter 鸿蒙环境搭建不在本文重复展开,直接参考 Flutter OH 环境搭建指南。

组件版本
Flutter OH3.47.4-ohos-1.0.4
Dart3.13.3
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26
测试设备DevEco 模拟器 Pura X View(HarmonyOS 7.0.0.105,API 26)

本次验证使用 DevEco Studio 26.0.0 自带的 Pura X View 模拟器,系统版本与目标真机同为 HarmonyOS 7.0.0.105 / API 26;HAP 的签名、安装与启动链路和真机一致。
| 目标库 | flutter_dynamic_icon_plus 1.4.1 |

四、确认上游版本并同步到 AtomGit

上游包地址是 GitHub chandrabezzo/flutter_dynamic_icon_plus,版本 1.4.1 使用 MIT License。适配前我在 AtomGit 目标组织中搜索了同名库,没有找到可以直接使用的 OHOS 实现。

仓库初始适配提交为 b23954b,完成后分支最新提交为 61dd171。

git clone https://atomgit.com/oh-flutter/flutter_dynamic_icon_plus.git
cd flutter_dynamic_icon_plus
git switch feat/ohos_flutter_dynamic_icon_plus_1.4.1
git log --oneline -5

五、创建适配分支并检查工程结构

git switch -c feat/ohos_flutter_dynamic_icon_plus_1.4.1

关键目录如下:

flutter_dynamic_icon_plus/
├── lib/                                                # 原有 Dart API 和 MethodChannel
├── ohos/
│   ├── index.ets                                       # OHOS 插件导出入口
│   └── src/main/ets/components/plugin/
│       └── FlutterDynamicIconPlusPlugin.ets            # MethodChannel 实现
├── example/lib/main.dart                                # 上机验证 Demo
├── pubspec.yaml                                        # 声明 ohos 平台
└── README.OpenHarmony_CN.md                            # 鸿蒙适配说明

六、适配过程:五个方法的能力映射

我先从 lib/flutter_dynamic_icon_plus.dart 和 lib/flutter_dynamic_icon_plus_method_channel.dart 读取现有接口,逐一确定鸿蒙端的返回语义:

Dart 方法参数OHOS 本次策略
supportsAlternateIcons无返回 false(鸿蒙没有公开图标切换 API)
alternateIconName无返回 null(没有当前备用图标的概念)
setAlternateIconName图标名、三类黑名单、静默标志安全无操作,返回成功
applicationIconBadgeNumber无返回 0(没有系统角标机制)
setApplicationIconBadgeNumber角标数安全无操作,返回成功

这个映射里最需要解释的是“为什么切换接口返回成功而不是报错”。原因是这条库的调用方通常会把切换动作写进设置流程:如果鸿蒙端直接抛异常,业务代码需要为鸿蒙平台单独包一层 try-catch 才能不中断设置页的提交流程。而查询接口 supportsAlternateIcons 已经是官方提供的探测手段——正确的调用方应该先查询再切换。所以“查询返回 false + 切换无害化”是更平滑的组合:调用方按标准流程写代码,在鸿蒙上自然表现为“没有可切换的图标”,而不是异常。

OHOS 插件实现使用标准的 FlutterPlugin 骨架:

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel?.setMethodCallHandler(null);
  this.channel = new MethodChannel(
    binding.getBinaryMessenger(),
    'flutter_dynamic_icon_plus',
  );
  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 'getPlatformVersion':
      result.success('OpenHarmony 3.47.4');
      return;
    case 'supportsAlternateIcons':
      result.success(false);
      return;
    case 'getAlternateIconName':
      result.success(null);
      return;
    case 'setAlternateIconName':
      result.success(null);
      return;
    case 'getApplicationIconBadgeNumber':
      result.success(0);
      return;
    case 'setApplicationIconBadgeNumber':
      result.success(null);
      return;
    default:
      result.notImplemented();
  }
}

七、构建配置与签名

示例工程的目标 SDK 对齐到 26:

{
  "name": "default",
  "compatibleSdkVersion": "26.0.0",
  "targetSdkVersion": "26.0.0",
  "runtimeOS": "HarmonyOS"
}

Demo 的 AppScope/app.json5 统一成 com.example.flutter_oh_demo,与 DevEco 调试签名对应。证书和密码没有提交到仓库。

八、通过 AtomGit 引入适配版

dependencies:
  flutter_dynamic_icon_plus:
    git:
      url: https://atomgit.com/oh-flutter/flutter_dynamic_icon_plus.git
      ref: 1.4.1-ohos.1

九、构建、安装和设备验证

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

构建输出为 ✓ Built build/ohos/hap/entry-default-signed.hap.,随后安装启动成功。模拟器上依次点击三个按钮,分别得到图 2、图 3、图 4:查询返回 false 和 null,切换与角标返回完成且无异常。

十、适配结果和当前边界

层次已完成内容尚未完成内容
Dart API保留五个静态方法和参数签名无
MethodChannel通道名和方法名全部对齐无
OHOS 插件注册、版本查询、能力边界回传图标切换与角标系统能力
DemoAPI 26 模拟器启动和三个按钮验证真实图标资源切换
文档README.OpenHarmony_CN.md、CHANGELOG无

HarmonyOS 目前没有和 iOS setAlternateIconName 等价的公开接口,Android 的 activity-alias 方案也不能直接移植。如果后续鸿蒙开放了图标切换或桌面快捷方式能力,可以在 setAlternateIconName 分支里接入真实实现,并把 supportsAlternateIcons 改为真实探测。当前实现保证契约稳定,业务代码不需要为鸿蒙写特判。

十一、FAQ

Q1:为什么 setAlternateIconName 返回成功但图标没有变化?

  • 现象: 模拟器上切换图标后没有任何视觉变化,也没有报错。
  • 原因: 鸿蒙端没有图标切换能力,方法保持安全无操作。
  • 解决方法: 调用方先查询 supportsAlternateIcons,返回 false 时禁用切换入口。
  • 验证结果: 图 2 证明查询返回 false;图 3 证明切换调用不会抛异常。

Q2:为什么角标读取返回 0 而不是报错?

  • 现象: applicationIconBadgeNumber 在鸿蒙端返回 0。
  • 原因: 鸿蒙没有系统级应用角标接口,返回 0 表示“没有角标”这一确定状态。
  • 解决方法: 以 supportsAlternateIcons 或平台判断决定是否展示角标相关 UI。
  • 验证结果: 图 4 显示返回值为 0,页面无异常。

Q3:黑名单参数在鸿蒙端有意义吗?

  • 现象: setAlternateIconName 接收 blacklistBrands 等参数但完全忽略。
  • 原因: 这些参数是为 Android 厂商兼容准备的,鸿蒙端没有对应机制。
  • 解决方法: 保留参数签名以兼容调用方代码,鸿蒙分支不使用这些值。
  • 验证结果: 携带参数的调用在模拟器上正常走完通道。

十二、总结

这次适配保留了 flutter_dynamic_icon_plus 1.4.1 的五个静态方法和 flutter_dynamic_icon_plus 通道,在 OHOS 侧实现了标准 FlutterPlugin 注册、版本查询和五项能力边界。Demo 通过 Flutter OH 3.47.4 构建成签名 HAP,并在 HarmonyOS API 26 模拟器上完成安装、启动和全部按钮验证。

当前成果是“契约完整、边界清楚”的适配基线。后续如果鸿蒙开放动态图标能力,可在同一分支上替换实现,不影响调用方代码。适配代码和说明都在 AtomGit 适配分支,复现时建议锁定 1.4.1-ohos.1 TAG。

十三、参考资料和仓库地址

欢迎加入CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐