Flutter 3.47 鸿蒙三方库适配实战:从 0 到 1 处理 dynamic_app_icon_flutter_plus

动态桌面图标是一个很容易被误判的功能。Android 可以通过 Activity Alias 切换图标,iOS 有 Alternate Icons API,但鸿蒙的应用图标由应用包和启动 Ability 配置管理,不能把 Android 的组件切换代码直接搬过去。

本文以 dynamic_app_icon_flutter_plus 2.2.1 为例,记录 Flutter 3.47 鸿蒙适配过程:先确认 Dart 方法名,再补齐 OHOS 插件注册,最后在鸿蒙端准确返回“当前平台不支持”,而不是伪造一个已经切换成功的图标。文章中的 HAP、安装和真机截图均来自实际验证。

一、最终效果

图 1:API 26 真机启动 Demo,页面显示当前平台不支持动态桌面图标。

图 2:点击重新检查后仍返回不支持,说明结果来自插件通道,而不是静态写在页面里。

验证点真机结果
Flutter3.47.4-ohos-1.0.4
HarmonyOS7.0.0.105,API 26
HAPsigned HAP 构建通过
能力检查false
可用图标空列表
真机结论明确返回当前平台不支持

二、先看原库提供哪些 API

原库公开了这些能力:

final supported =
    await DynamicAppIconFlutterPlus.supportsAlternateIcons;

final current =
    await DynamicAppIconFlutterPlus.getAlternateIconName();

final icons =
    await DynamicAppIconFlutterPlus.getAvailableIcons();

await DynamicAppIconFlutterPlus.setAlternateIconName('holiday');
await DynamicAppIconFlutterPlus.applyPendingIcon();

对应的 MethodChannel 方法名是:

Dart APIMethodChannel 方法
supportsAlternateIconsmSupportsAlternateIcons
getAlternateIconName()mGetAlternateIconName
getAvailableIcons()mGetAvailableIcons
setAlternateIconName()mSetAlternateIconName
applyPendingIcon()mApplyPendingIcon

适配前先把这些名字列出来很重要。原仓库中的平台接口文件和业务入口文件并不一定使用同一组方法名,直接照着类名写 ArkTS,容易漏掉 m 前缀。

三、为什么鸿蒙端不能直接照搬

Android 的动态图标通常依赖:

  • Activity Alias;
  • PackageManager.setComponentEnabledSetting();
  • Android Manifest 中配置多组图标;
  • Launcher 重新加载组件状态。

iOS 使用系统提供的 Alternate Icons 能力。鸿蒙端的启动图标和应用 Ability 配置属于应用包的一部分,当前这条 Flutter 插件链没有一个可以直接对应上述调用的公开跨平台 API。

因此本次适配的边界是:

能力OHOS 结果
查询是否支持动态图标返回 false
查询当前备用图标返回 null
查询可用图标列表返回空列表
切换图标返回 UNSUPPORTED
应用启动使用包内默认图标,不受影响

“返回不支持”是适配结果的一部分。它能让业务层决定是否隐藏换图入口,而不是让用户点击后看到一个假的成功提示。

四、准备工具和真机

本文实际环境:

工具版本
Flutter OH3.47.4-ohos-1.0.4
Dart3.13.3
DevEco Studio26.0.0 Release
HarmonyOS SDKAPI 26
测试设备127.0.0.1:5555,HarmonyOS 7.0.0.105

没有 Flutter 鸿蒙环境时,先看 Flutter OH 环境搭建指南。

export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter --version
hdc list targets

确认 Flutter 版本包含 3.47.4-ohos,并确认 hdc list targets 能看到 ohos-arm64。

五、拉取适配分支

本文使用的 AtomGit 信息:

git clone https://atomgit.com/oh-flutter/dynamic_app_icon_flutter_plus.git
cd dynamic_app_icon_flutter_plus
git switch feat/ohos_dynamic_app_icon_flutter_plus_2.2.1
git log -1 --oneline

适配文章和示例代码都锁定这个分支,避免远端继续变化后方法名或文档发生偏移。

六、注册 OHOS 平台

在 pubspec.yaml 中声明:

flutter:
  plugin:
    platforms:
      android:
        package: com.rishya.dynamic_app_icon_flutter_plus
        pluginClass: DynamicAppIconFlutterPlusPlugin
      ios:
        pluginClass: DynamicAppIconFlutterPlusPlugin
      ohos:
        pluginClass: DynamicAppIconFlutterPlusPlugin

OHOS 入口:

import DynamicAppIconFlutterPlusPlugin from './src/main/ets/components/plugin/DynamicAppIconFlutterPlusPlugin';

export default DynamicAppIconFlutterPlusPlugin;

如果没有 ohos 声明,Flutter 仍然会把它当成只有 Android/iOS 的插件;即使 ArkTS 文件存在,运行时也不会被注册。

七、逐个对齐 MethodChannel 方法名

Dart 端调用的是:

_channel.invokeMethod('mSupportsAlternateIcons');
_channel.invokeMethod('mGetAlternateIconName');
_channel.invokeMethod('mGetAvailableIcons');
_channel.invokeMethod('mSetAlternateIconName', arguments);
_channel.invokeMethod('mApplyPendingIcon');

OHOS 端必须使用完全一致的字符串:

switch (call.method) {
  case 'mSupportsAlternateIcons':
    result.success(false);
    return;
  case 'mGetAvailableIcons':
    result.success([]);
    return;
  case 'mGetAlternateIconName':
    result.success(null);
    return;
}

这次适配过程中发现,早期 OHOS 骨架曾经写成了 supportsAlternateIcons 和 getAlternateIconName,而 Dart 实际发送的是带 m 前缀的方法。这个问题不会在 HAP 编译阶段暴露,只会在设备运行时表现为能力调用失败,所以必须从 Dart 的 invokeMethod 反向核对 ArkTS。

八、处理不支持的切换请求

查询能力可以返回 false,但真正调用切换时,最好返回带错误码的结果:

case 'mSetAlternateIconName':
case 'mApplyPendingIcon':
  result.error(
    'UNSUPPORTED',
    'Dynamic launcher icons are not available on OpenHarmony.',
    null,
  );
  return;

Dart 层可以捕获:

try {
  await DynamicAppIconFlutterPlus.setAlternateIconName('holiday');
} on PlatformException catch (error) {
  debugPrint(error.code);
}

这样业务层可以:

  • 隐藏“更换图标”按钮;
  • 显示“当前平台暂不支持”;
  • 使用静态主题设置作为替代;
  • 不把失败当成图标已经切换。

九、创建最小 Demo

Demo 启动时自动检查两个信息:

final supported =
    await DynamicAppIconFlutterPlus.supportsAlternateIcons;
final icons =
    await DynamicAppIconFlutterPlus.getAvailableIcons();

setState(() {
  _status = supported ? '支持动态图标' : '当前平台不支持';
  _detail = '可用图标数量:${icons.length}';
});

页面同时提供“重新检查”按钮,确保结果确实来自 MethodChannel,而不是写死在页面文本中。真机上每次检查都得到:

当前平台不支持
可用图标数量:0

十、构建、签名和安装

无签名构建:

flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign

配置 DevEco 调试签名后:

flutter build hap --debug
hdc list targets
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.flutter_oh_demo

本文使用签名 HAP 在 API 26 真机上验证。证书密码和签名文件没有提交。

十一、真机验证

启动页面后,插件自动查询 mSupportsAlternateIcons 和 mGetAvailableIcons。页面显示“当前平台不支持”和“可用图标数量:0”。

点击“重新检查”后,页面仍然显示相同结果。这个过程验证了:

  1. Dart 方法名正确;
  2. OHOS 插件已注册;
  3. MethodChannel 请求能到达 ArkTS;
  4. ArkTS 返回值能被 Dart 转换并更新 UI;
  5. 鸿蒙端没有伪造动态桌面图标能力。

如果业务继续调用 setAlternateIconName,会收到 UNSUPPORTED,而不是一个假的成功结果。

十二、常见问题

Q1:为什么不直接返回 true

因为鸿蒙端没有完成动态图标切换。返回 true 会让用户以为图标已经改变,重新回到桌面后才发现没有效果,问题更难排查。

Q2:为什么可用图标列表为空

动态备用图标需要应用包中存在对应的图标资源和启动配置。当前 OHOS 适配没有建立这套动态切换协议,因此返回空列表。

Q3:为什么方法名要带 m 前缀

这是上游 Dart 实现定义的通道契约。原生端必须匹配真实的 invokeMethod 字符串,不应该根据方法含义自行改名。

Q4:静态应用图标能不能正常显示

可以。本文只限制运行时切换能力,应用默认图标仍然由 AppScope 和应用包配置提供。

Q5:以后要支持动态图标还需要做什么

需要先确认 HarmonyOS 当前版本是否提供公开的动态应用图标能力,再设计资源注册、Ability 配置、桌面刷新和生命周期处理,最后逐个 API 做真机验证。

十三、复现命令

git clone https://atomgit.com/oh-flutter/dynamic_app_icon_flutter_plus.git
cd dynamic_app_icon_flutter_plus
git switch feat/ohos_dynamic_app_icon_flutter_plus_2.2.1
export PATH=/Users/von/Developer/flutter-oh-3.47.4/bin:$PATH
flutter pub get
flutter analyze
cd example
flutter test
flutter build hap --debug --no-codesign

十四、总结

这次适配的重点是把“能力不存在”变成可处理的接口结果。Dart API、MethodChannel 和 OHOS 插件都保留了,查询接口返回明确的 false 和空列表,切换接口返回 UNSUPPORTED。新手处理类似插件时,应先确认系统能力,再决定是接入原生 API、返回平台限制,还是设计业务替代方案。

十五、参考资料与仓库地址

本文适配仓库

上游和包信息

Flutter 和鸿蒙资料

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

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐