前言

harmony_icons 是把鸿蒙官方 HM Symbol 图标字体搬上 Flutter 的零依赖三方库:534 个官方图标、17 个分类、每个图标自带中文名/模块/最低系统版本等元数据,用法与 cupertino_icons 完全一致。上一篇《harmony_icons 全解》讲清了它的背景、架构与鸿蒙适配原理;这一篇只谈"怎么用"——站在应用开发者的视角,把六大典型用法逐一拆解成可直接抄进项目的代码:

  • 静态常量:工具栏、Tab、悬浮按钮里的图标怎么选、怎么调;
  • 数据驱动:服务端下发图标名怎么安全渲染;
  • 图标市场:遍历、分类筛选、搜索匹配的完整实现;
  • 元数据驱动 UI:中文名、模块、版本兼容性提示怎么上界面;
  • 高级渲染:RTL 镜像与 wght 可变字重;
  • 实战页面:把以上能力组合进真实页面,并在鸿蒙模拟器上运行验证。

文中所有图标名(如 OhosIcons.person_badge_plus、OhosIcons.checkmark_circle_fill)均核对过仓库生成源码,真实存在,可直接复制运行。

建议搭配阅读:《Flutter三方库适配OpenHarmony【图标库】HarmonyOS Symbol官方图标字体上Flutter:harmony_icons 全解》,本文是其"使用篇"。

一、阅读本文前:先明确你要解决哪类问题

1.1 图标库的三种使用姿势

拿到一个图标库,开发者通常要解决三类问题:

姿势典型场景对应本文章节
写死常量固定 UI:按钮、Tab、空状态、角标三
动态取图标服务端配置、路由表、低代码下发图标名四
批量浏览/选择图标市场、主题选择器、图标预览工具五

harmony_icons 的 API 恰好为这三类姿势各留了专门的入口:OhosIcons.<name> 常量、OhosIcons.fromName(name)、OhosIcons.values / categories / info。第六、七章再补充元数据展示与高级渲染,第八章给出一个把全部能力串起来的实战页面。

1.2 为什么鸿蒙上的图标用法值得单独讲

在官方 Flutter 里用图标库早就有成熟套路,但在鸿蒙(OpenHarmony)上,有两个额外变量值得先交代:

  1. 平台链路不同:鸿蒙分叉 CPF-Flutter/flutter_flutter 的字体加载、HAP 打包链路与 Android/iOS 不同,图标字体能否正确进包是第一个要验证的点;
  2. 视觉标准不同:鸿蒙应用期望使用 HarmonyOS Symbol 这套与系统同源的图标语言,而不是 Material/Cupertino 风格——这正是 harmony_icons 存在的意义。

好消息是:由于本库是纯 Dart + 字体实现,Dart 侧的写法在两个平台上完全一致,平台差异只存在于构建与验证环节(第二章会给出验证方法)。

二、使用前准备:依赖、环境与冒烟验证

2.1 添加依赖

在 pubspec.yaml 中加入依赖并拉取:

dependencies:
  harmony_icons: ^0.1.0
flutter pub get

导入只需一行,库入口会导出全部 API:

import 'package:harmony_icons/harmony_icons.dart';

字体在库的 pubspec 中已声明(family 固定为 HMSymbol,asset 为子集化后的 assets/fonts/HMSymbol.ttf),使用方无需任何字体配置。

2.2 冒烟验证:先确认字体加载正常

图标库出问题的经典症状是"图标变成方块/问号"。写业务代码之前,先用 10 行代码做冒烟验证,一次性确认字体加载、码点映射、fontPackage 跨包引用三件事:

class IconSmokeTest extends StatelessWidget {
  const IconSmokeTest({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // 静态常量
          Icon(OhosIcons.wifi, size: 40),
          // 动态查询
          Icon(OhosIcons.fromName('airplane_fill'), size: 40),
          // 元数据
          Text('count=${OhosIcons.count} v${OhosIcons.version} '
              'cat=${OhosIcons.categories.length}'),
        ],
      ),
    );
  }
}

运行后的判定标准:

  1. 两个图标正常渲染字形(不是方块 □、不是 ?、不是空白);
  2. 文本输出 count=534 v2.3 cat=17(图标总数、数据版本、分类数);
  3. 控制台无字体加载相关异常。

提示:如果在鸿蒙工程(example/ohos 这类壳工程)上冒烟失败,先确认 ohos 壳执行过 ohpm install 且整包重新构建——字体是随 HAP 资源打包的,旧构建产物里可能没有新字体。

症状最可能原因处理
图标是方块/问号字体未进包 / fontPackage 被覆盖重新构建;检查是否手写了 fontPackage: null
图标是空白(无方块)码点映射错误确认用的是 OhosIcons 常量或 fromName,不要手写码点
fromName 抛 ArgumentError图标名不存在先 OhosIcons.info.containsKey(name) 判断(见第四章)
某图标渲染成别的图标误用镜像码点检查是否混用了 mirrorIcon

三、用法一:静态图标常量(最高频)

3.1 在 Icon 与 IconButton 中使用

绝大多数场景就是"固定位置放一个固定图标",直接用 OhosIcons.<name> 常量:

Scaffold(
  appBar: AppBar(
    title: const Text('消息'),
    actions: [
      IconButton(
        icon: Icon(OhosIcons.bell, size: 22),
        onPressed: _openNotifications,
      ),
    ],
  ),
  floatingActionButton: FloatingActionButton(
    tooltip: '新增联系人',
    child: Icon(OhosIcons.person_badge_plus, size: 24),
    onPressed: _addContact,
  ),
)

命名规则与官网一致:英文小写下划线(airplane_fill、battery_100percent),_fill 后缀表示实心变体(如 bell 空心 / bell_fill 实心)。拿不准有哪些名字时,两个查找渠道:

  • IDE 补全:输入 OhosIcons. 即可看到全部 534 个常量,每个常量的文档注释带"中文名 · 英文名 · 最低版本";
  • gallery 示例:仓库 example/ 自带图标市场应用(第八章有运行截图),支持中英文名搜索与分类筛选。

3.2 尺寸与颜色控制

OhosIcons 常量就是标准 IconData,Icon 的所有参数照常用:

// 状态栏风格的电池指示
Row(
  children: [
    Icon(OhosIcons.battery_100percent, size: 18, color: Colors.grey.shade600),
    Icon(OhosIcons.wifi, size: 16, color: Colors.grey.shade600),
    Icon(OhosIcons.airplane_fill, size: 16, color: Colors.orange),
  ],
)
  • size:像素尺寸,跟随字体渲染,任意缩放不失真(矢量字形);
  • color:着色,单色图标可染成任意颜色;
  • 主题色建议:跟随 Theme.of(context).colorScheme,而不是写死 Colors.x,保证深色模式下自动适配。

3.3 常见 UI 场景速查

场景推荐图标说明
返回 / 前进导航arrow_left / arrow_right、chevron_left / chevron_right细箭头 vs 小 V,按视觉重量选
刷新 / 重试arrow_2_circlepath、arrow_clockwise双圈循环 / 单圈循环
成功 / 失败 / 警示checkmark_circle_fill、xmark_circle、exclamationmark_triangle结果态三件套
空状态magnifyingglass、questionmark_circle搜索无结果 / 待补充信息
收藏 / 点赞star / star_fill、heart / heart_fill用 fill 变体表达"已激活"
隐私 / 安全lock、bell_slash_fill锁定 / 免打扰

经验法则:同一界面里"空心↔实心"成对出现时,用同一图标名的两个变体(bell↔bell_fill),视觉一致性最好;不要拿两个相似但不同源的图标混搭。

四、用法二:数据驱动的动态图标(fromName)

4.1 fromName 与安全查询

当图标名来自外部数据(服务端字段、路由表、JSON 配置)时,不能直接写常量,要用 fromName:

/// 服务端下发 {"icon": "bell_fill", "title": "通知"}
Icon(
  OhosIcons.fromName(serverIconName),
  size: 20,
)

fromName 的实现对"不存在的名字"零容忍——直接抛 ArgumentError:

static IconData fromName(String name) {
  return info[name]?.icon ??
      (throw ArgumentError.value(name, 'name', 'unknown symbol'));
}

所以生产代码里推荐"先查后取"的安全查询封装:

/// 安全版:名称不存在时返回 fallback,不抛异常
IconData iconByName(String name, {IconData fallback = OhosIcons.info_circle}) {
  return OhosIcons.info[name]?.icon ?? fallback;
}

4.2 服务端驱动 / 路由配置

一个典型的"消息中心"配置模型,图标名作为普通字符串下发:

class NavItem {
  const NavItem(this.title, this.iconName);
  final String title;
  final String iconName; // 服务端下发,如 'envelope'
}

final items = const [
  NavItem('收件箱', 'envelope'),
  NavItem('电话记录', 'phone'),
  NavItem('星标', 'star_fill'),
  NavItem('下载', 'download'),
];

// 列表渲染
ListTile(
  leading: Icon(iconByName(item.iconName), size: 22),
  title: Text(item.title),
  trailing: const Icon(OhosIcons.chevron_right, size: 18),
)

4.3 兜底与灰度策略

数据驱动的图标必须设计兜底,否则一个脏数据就能让界面崩掉或出现方块:

  1. 默认兜底:所有查询都带 fallback(如 info_circle 或 questionmark_circle);
  2. 白名单校验:数据入库/入缓存时先 OhosIcons.info.containsKey(name) 过滤,脏数据在渲染前就被拦下;
  3. 版本感知:OhosIconMeta.supportVersion 记录了每个图标的最低系统版本(本库 534 个图标中 525 个为 HarmonyOS 5.0+,少数为 5.1+/6.0/6.1),对低版本设备可用元数据判断是否需要降级替换。

注意:fromName 抛异常是特性不是缺陷——开发期尽早暴露配置错误。生产环境的稳健性由"安全查询 + 白名单"这一层保障,而不是改库的语义。

五、用法三:图标市场 / 选择器(遍历与筛选)

5.1 遍历全部图标

OhosIcons.values 返回全部 OhosIconMeta 的可迭代集合,"全量渲染"只需要一个 GridView:

GridView.builder(
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 120, childAspectRatio: 1,
  ),
  itemCount: OhosIcons.values.length,
  itemBuilder: (context, index) {
    final meta = OhosIcons.values.elementAt(index);
    return Icon(meta.icon, size: 32);
  },
)

5.2 按分类筛选

官方 17 个分类(系统UI、时间、编辑、通信、人物、交通出行、办公文件、连接……)可直接驱动筛选栏:

// 全部分类(已排序去重)
final categories = OhosIcons.categories; // 17 个

// 筛选"通信"分类
final communication = OhosIcons.values
    .where((m) => m.categories.contains('通信'))
    .toList();

注意 OhosIconMeta.categories 是复数列表:一个图标可以属于多个分类(如 wifi 同属 系统UI 与 连接),所以用 contains 而不是 ==。

5.3 搜索匹配(英文名 + 中文名双路)

图标市场最有用的功能是"按中文名搜图标"——这正是元数据里 label 的价值:

List<OhosIconMeta> filter(String query) {
  final q = query.trim().toLowerCase();
  return OhosIcons.values.where((m) {
    if (q.isEmpty) return true;
    return m.name.toLowerCase().contains(q) || m.label.contains(q);
  }).toList()
    ..sort((a, b) => a.name.compareTo(b.name));
}

双路匹配的好处:设计师说"给我个飞行模式的图标",开发者输入"飞行模式"即可命中 airplane_fill,不需要先知道英文名。

提示:OhosIcons.values 每次访问都遍历 info 的 values,筛选/排序逻辑在数据量 534 时开销可忽略;若在极高频列表里重复调用,可先缓存 List.of(OhosIcons.values) 再过滤。

运行截图

请添加图片描述

相关资源:

Logo

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

更多推荐