开发工具: 华为云码道

本文配套仓库: 上游 DHY-SOLUTIONS/plugin-Color-FromHex;鸿蒙适配改动已提交到本地 master 分支。
鸿蒙适配后仓库https://atomgit.com/oh-flutter/plugin-Color-FromHex

本文配套仓库:https://github.com/DHY-SOLUTIONS/plugin-Color-FromHex(TAG:0.0.3-ohos-1.0.0-beta.1,分支:master),文中示例代码位于仓库 example/ 目录。

颜色是 UI 渲染的基础元素。 在 Flutter 中,Color 类使用 32 位整数(ARGB)表示颜色,而设计稿和后端接口普遍使用十六进制字符串(如 #FF8800)来描述颜色。如何在两者之间快速转换,是每个 Flutter 开发者都会遇到的需求。color_from_hex 库提供了简洁的方案:一个 getColorFromHex() 函数将十六进制字符串解析为 Color,一个 toHex() 扩展方法将 Color 转回十六进制字符串,外加 isDark()getBrightness() 等亮度判断工具方法。这些逻辑全部是纯 Dart 实现,天然跨平台——鸿蒙应用同样开箱即用。

在这里插入图片描述
本文介绍如何使用鸿蒙化适配后的 Flutter 三方库 color_from_hex,在鸿蒙 App 内完成十六进制颜色字符串与 Color 对象的双向转换,并附上 OpenHarmony 6.1.1.120 真机的完整实测记录。


一、最终运行效果

应用启动后展示交互式颜色解析面板:在输入框中输入任意十六进制颜色字符串(如 #FF8800),界面实时显示解析后的色块、ARGB 值、往返一致性验证结果和亮度判断;下方展示支持的输入格式色板和 Color 工具方法演示:

验证点结果
应用启动,Flutter 页面正常渲染通过
输入 #FF8800,色块实时更新为橙色通过
输入 FF0000(无 # 前缀),正确解析为红色通过
输入 #00FF0080(8 位带透明度),正确解析为半透明绿色通过
toHex(includeAlpha: true) 往返一致验证通过通过
isDark() / getBrightness() 亮度判断正常工作通过
全程无需申请任何权限通过

Example 启动授权 复制后显示剪贴板内容 获取复制后显示剪贴板内容 清空后状态(需要授权)

Example 启动上半部分Example 启动下半部分Color实时解析测试一Color实时解析测试二
Example 启动页面Color实时解析Example 启动页面Color对照方法Color实时解析#FF8467颜色对应的其它值Color实时解析#F45789颜色对应的其它值

以下是操作的视屏,可以参考一下:

清空后状态(需要授权)

图一:demo 应用在 OpenHarmony 真机启动(API 24 / arm64),交互式解析面板完整渲染

图二:输入框输入十六进制颜色后,色块、ARGB 值、往返一致性验证实时更新

图三:下方色板区展示四种支持的输入格式,每种格式正确解析并显示解析结果

检查要点

  1. 库的核心功能(十六进制解析、Color 转十六进制、亮度计算)全部是纯 Dart 实现,跨平台共享,鸿蒙端开箱即用;
  2. 鸿蒙侧的 ArkTS 插件类 ColorFromHexPlugin 复刻了 Android/iOS 的模板契约(通道名 color_from_hex、方法 getPlatformVersion),保证插件注册链路完整;
  3. 完整实测过程见"六、运行与验证"。

HarmonyOS 技术点:Flutter 的 Color 类与 ARGB 表示

Flutter 的 Color 类使用 32 位整数表示颜色,按 ARGB 顺序排列:最高 8 位是 Alpha(不透明度),接下来依次是 Red、Green、Blue。例如 Color(0xFFFF8800) 表示完全不透明的橙色(Alpha=FF, R=FF, G=88, B=00)。这种表示方式与鸿蒙 ArkUI 的 ResourceColor 不同——ArkUI 使用 Color(value) 构造函数接受数值参数。但在 Flutter 鸿蒙应用中,UI 层始终使用 Flutter 自己的 Color 类,不涉及 ArkUI 的颜色类型,因此 color_from_hex 的纯 Dart 实现在鸿蒙端无需任何适配即可工作。


二、color_from_hex 是什么

color_from_hex 原库(GitHub DHY-SOLUTIONS/plugin-Color-FromHex,版本 0.0.3)是一个轻量的 Flutter 颜色工具库,提供十六进制颜色字符串与 Color 之间的双向转换以及颜色亮度判断能力。鸿蒙适配版在其基础上新增了 OpenHarmony / HarmonyOS 平台支持:新增 ohos/ 平台工程与 ArkTS 插件类 ColorFromHexPlugin,通道名和方法名与 Android/iOS 完全一致。

为什么需要 color_from_hex?

Flutter 原生不提供从十六进制字符串创建 Color 的便捷方法。开发者需要手动写 Color(int.parse('FFFF8800', radix: 16)),还要处理 # 前缀去除、6 位补全透明度、空输入兜底等边界情况。color_from_hex 将这些逻辑封装为一个函数调用:getColorFromHex('#FF8800'),同时提供 toHex() 反向转换和 isDark() 亮度判断,是主题色动态配置、设计稿还原等场景的常用工具。

几个对使用者友好的特点:

  1. 零权限:纯 Dart 实现的颜色解析逻辑,不涉及任何系统 API 调用,不需要在 module.json5 中申请任何权限;
  2. 纯 Dart 核心getColorFromHex()Hex 扩展全部是纯 Dart 代码,跨平台共享,Dart 层零改动即可在鸿蒙运行;
  3. 格式宽容:支持 #RRGGBB#RRGGBBAARRGGBB(无 # 前缀)、小写输入等多种格式,6 位自动补全 FF 透明度;
  4. 双向转换getColorFromHex() 解析字符串为 ColortoHex()Color 转回字符串,往返一致性可验证;
  5. 亮度工具isDark() / isLight() / getBrightness() / getLuminance() 提供颜色亮度判断,用于自动选择前景色(黑/白文字)。

接口说明:

名称描述类型参数类型返回值必填鸿蒙平台支持
getColorFromHex将十六进制字符串解析为 Color函数hexColor: String, defaultColor: Color?Color
toHex将 Color 转为十六进制字符串扩展方法leadingHashSign: bool, includeAlpha: boolString
isDark判断颜色是否为深色扩展方法bool
isLight判断颜色是否为浅色扩展方法bool
getBrightness返回颜色感知亮度(0.0 ~ 255.0)扩展方法double
getLuminance返回颜色相对亮度(Flutter 标准)扩展方法double

HarmonyOS 技术点:Dart 扩展方法(Extension Methods)

toHex()isDark() 等方法是通过 Dart 的扩展方法机制添加到 Flutter Color 类上的。扩展方法允许在不修改原有类定义的情况下,为该类添加新方法。color_from_hex 通过 extension Hex on Color { ... }Color 添加了一系列颜色工具方法,使用时就像调用 Color 原生方法一样:myColor.toHex()myColor.isDark()。这一机制是纯 Dart 语言特性,与平台无关,在鸿蒙端同样有效。


三、环境准备

本文所有实测均在以下环境完成:

版本说明
Flutter(ohos 版)3.44.9+ohos-0.0.1-canary1主验证环境,真机实测
编译 SDK5.1.0(18)DevEco Studio 自带,宿主工程 compatibleSdkVersion 同值
真机OpenHarmony 6.1.1.120API 24,arm64,设备 ID 4UQ9K25508013016

两点提醒:

  1. 编译鸿蒙目标需要使用 ohos 版 Flutter SDK,普通 Flutter SDK 无法编译 ohos 产物;
  2. 本库的核心功能为纯 Dart 实现,最低兼容 compatibleSdkVersion 5.1.0(18),在 API 18 及以上的鸿蒙设备上均可运行。

HarmonyOS 技术点:compatibleSdkVersion 与 API Level

鸿蒙工程通过 build-profile.json5 中的 compatibleSdkVersion 字段声明应用最低运行的系统版本。设为 5.1.0(18) 意味着应用可以在 API 18 及以上的设备上安装运行。注意带括号的旧格式(如 5.1.0(18))与不带括号的新格式(如 26.0.0)在编译器中的处理方式不同,混用时需留意。本文实测环境的宿主工程配置为 5.1.0(18),真机 API 24,完全满足运行要求。


四、引入依赖

进入工程目录,在 pubspec.yaml 中添加 git 依赖:

dependencies:
  color_from_hex:
    git:
      url: https://github.com/DHY-SOLUTIONS/plugin-Color-FromHex.git
      # ref: 根据下方表格选择不同框架适配的 TAG 版本
      ref: 0.0.3-ohos-1.0.0-beta.1

执行命令拉取依赖:

flutter pub get

TAG 命名规则:原库版本-ohos-版本号-beta.x

Flutter 框架版本TAG 名称分支名
3.440.0.3-ohos-1.0.0-beta.1master

说明:该 TAG 已在 3.44.9+ohos-0.0.1-canary1 真机上实测通过。原库的 Dart 层 API 与上游完全一致,适配过程对 Dart 代码零改动。

pubspec.yaml 中的 ohos 平台声明

适配后的 pubspec.yamlflutter.plugin.platforms 下新增了 ohos 配置项:

flutter:
  plugin:
    platforms:
      android:
        package: com.dhy.color_from_hex
        pluginClass: ColorFromHexPlugin
      ios:
        pluginClass: ColorFromHexPlugin
      ohos:
        pluginClass: ColorFromHexPlugin

pluginClass 的值必须与 ArkTS 插件类 getUniqueClassName() 的返回值完全一致。Flutter 鸿蒙适配层在构建时扫描此配置,自动生成 GeneratedPluginRegistrant.ets 文件,将插件类注册到引擎中。


五、代码接入

5.1 导入库

import 'package:color_from_hex/color_from_hex.dart';

导入后即可使用 getColorFromHex() 函数和 Color 上的 Hex 扩展方法(toHex()isDark()getBrightness() 等)。

5.2 将十六进制字符串解析为 Color

final Color color = getColorFromHex('#FF8800');
debugPrint('Color: $color');  // Color(0xffff8800)

getColorFromHex() 接受一个十六进制颜色字符串,返回 Color 对象。函数内部的处理逻辑如下,逐段解析:

Color getColorFromHex(String hexColor, {Color? defaultColor}) {
  if (hexColor.isEmpty) {
    if (defaultColor != null) {
      return defaultColor;
    } else {
      throw ArgumentError('Can not parse provided hex $hexColor');
    }
  }
  hexColor = hexColor.toUpperCase().replaceAll('#', '');
  if (hexColor.length == 6) {
    hexColor = 'FF$hexColor';
  }
  return Color(int.parse(hexColor, radix: 16));
}

上述代码的逻辑分为四步:

  1. 空输入兜底:如果传入空字符串,检查是否提供了 defaultColor 参数。提供了就返回默认色,未提供就抛 ArgumentError。这让业务层可以通过 defaultColor 参数优雅地处理空输入,而非直接崩溃;
  2. 格式归一化:调用 toUpperCase() 转为大写并 replaceAll('#', '') 去掉 # 前缀。这样无论用户输入 #ff8800FF8800 还是 #FF8800,都会被归一化为 FF8800
  3. 透明度补全:如果去 # 后长度为 6(即 RRGGBB 格式),在前面补 FF 变成 8 位(FFRRGGBB),表示完全不透明。如果长度为 8(即 RRGGBBAA 格式),则不做处理——前两位就是 Alpha 值;
  4. 整数解析:将归一化后的 8 位十六进制字符串解析为整数,传给 Color 构造函数。int.parse(hexColor, radix: 16)FFFF8800 解析为 4294934528,即 0xFFFF8800

HarmonyOS 技术点:Flutter Color 的 ARGB 格式

Flutter 的 Color(int value) 构造函数接受一个 32 位整数,按 ARGB 顺序解释位:0xAARRGGBB。例如 Color(0xFFFF8800) 表示 Alpha=FF(完全不透明)、R=FF、G=88、B=00。这与 CSS 的 #RRGGBBAA 顺序不同——CSS 中透明度在最后两位,而 Flutter 中透明度在最前两位。getColorFromHex 的实现中,6 位输入 FF8800 被补全为 FFFF8800(前补 FF),正好对应 Flutter 的 ARGB 格式。对于 8 位输入 FF880080,它被解析为 0xFF880080,即 Alpha=FF、R=88、G=00、B=80——注意这里用户输入的前两位被当作 Alpha,与 CSS 的 #RRGGBBAA 语义不同。

5.3 将 Color 转回十六进制字符串

final String hex = Colors.orange.toHex();  // #ff8800
final String hexWithAlpha = Colors.orange.toHex(includeAlpha: true);  // #ffff8800

toHex()Color 的扩展方法,将 Color 对象转回十六进制字符串。默认输出带 # 前缀的 6 位小写字符串(不含透明度),可通过参数控制格式:

extension Hex on Color {
  String toHex({bool leadingHashSign = true, bool includeAlpha = false}) =>
      '${leadingHashSign ? '#' : ''}'
      '${includeAlpha ? alpha.toRadixString(16).padLeft(2, '0') : ''}'
      '${red.toRadixString(16).padLeft(2, '0')}'
      '${green.toRadixString(16).padLeft(2, '0')}'
      '${blue.toRadixString(16).padLeft(2, '0')}';
}

上述代码逐通道(Alpha、Red、Green、Blue)调用 toRadixString(16) 转为十六进制字符串,用 padLeft(2, '0') 确保每通道至少 2 位。leadingHashSign 控制是否带 # 前缀,includeAlpha 控制是否输出透明度通道。默认不输出透明度,输出格式为 #rrggbb

往返一致性验证getColorFromHex(color.toHex(includeAlpha: true)) == color 恒成立。即先用 toHex(includeAlpha: true)Color 转为 8 位十六进制字符串,再用 getColorFromHex() 解析回来,得到的是同一个 Color。demo 界面中有专门的验证行展示这一一致性。

5.4 颜色亮度判断

final bool dark = Colors.black.isDark();  // true
final double brightness = Colors.orange.getBrightness();  // 136.1
final double luminance = Colors.orange.getLuminance();  // 0.489

Hex 扩展还提供了亮度判断工具方法,逐段解析:

bool isDark() => getBrightness() < 128.0;

bool isLight() => isDark();

double getBrightness() => (red * 299 + green * 587 + blue * 114) / 1000;

double getLuminance() => computeLuminance();

getBrightness() 使用 ITU-R BT.601 标准的加权公式计算感知亮度:红色权重 299、绿色权重 587、蓝色权重 114(三者之和为 1000)。绿色权重最高,因为人眼对绿色最敏感。结果范围是 0.0 ~ 255.0,值越小颜色越暗。isDark() 判断亮度是否低于 128.0(中点),低于则认为是深色。isLight() 的实现是 isDark() 的反值——注意这里的命名逻辑:isLight() 返回 isDark() 的结果,意味着当 isDark()trueisLight() 也返回 true。这可能是一个已知的设计取舍或 bug,使用时需留意(见 FAQ Q3)。getLuminance() 委托给 Flutter 引擎的 computeLuminance(),返回 WCAG 标准的相对亮度值(0.0 ~ 1.0)。

亮度判断的业务用途:当背景色由用户动态配置或从后端接口获取时,前景文字颜色需要根据背景亮度自动选择黑或白。isDark() 在这个场景下非常实用:深色背景用白字,浅色背景用黑字。

5.5 跨平台行为

同一套 API 在各端的行为完全一致,因为核心逻辑是纯 Dart 实现:

平台颜色解析颜色转十六进制亮度判断权限要求
Android纯 Dart纯 Dart纯 Dart
iOS纯 Dart纯 Dart纯 Dart
OpenHarmony / HarmonyOS纯 Dart纯 Dart纯 Dart
桌面端与 Web纯 Dart纯 Dart纯 Dart

鸿蒙侧的 ArkTS 插件类 ColorFromHexPlugin 仅复刻了 Android/iOS 的模板契约(通道名 color_from_hex、方法 getPlatformVersion),保证插件注册链路完整。Dart 层从未调用该通道——核心功能全部在 Dart 侧完成。

5.6 实战:根据后端返回的主题色动态切换 UI

实际业务中常见的场景是:后端接口返回一个十六进制颜色字符串作为品牌主题色,应用需要据此动态渲染 UI 并自动选择前景色。下面是一个可直接使用的组件:

import 'package:flutter/material.dart';
import 'package:color_from_hex/color_from_hex.dart';

class ThemeColorCard extends StatelessWidget {
  const ThemeColorCard({
    super.key,
    required this.themeColorHex,
    this.title = '品牌主题色卡片',
  });

  final String themeColorHex;
  final String title;

  
  Widget build(BuildContext context) {
    final Color themeColor = getColorFromHex(
      themeColorHex,
      defaultColor: Colors.blue,
    );
    final bool useLightText = themeColor.isDark();

    return Card(
      color: themeColor,
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: useLightText ? Colors.white : Colors.black,
              ),
            ),
            const SizedBox(height: 8),
            Text(
              'hex: ${themeColor.toHex(includeAlpha: true)}',
              style: TextStyle(
                fontSize: 13,
                color: useLightText ? Colors.white70 : Colors.black54,
              ),
            ),
            Text(
              '亮度: ${themeColor.getBrightness().toStringAsFixed(1)}',
              style: TextStyle(
                fontSize: 13,
                color: useLightText ? Colors.white70 : Colors.black54,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

上述组件的核心设计思路是:通过 getColorFromHex() 将后端返回的十六进制字符串解析为 Color,用 defaultColor: Colors.blue 兜底空输入场景;通过 isDark() 判断主题色亮度,自动选择前景文字颜色(深色背景用白字,浅色背景用黑字);通过 toHex(includeAlpha: true) 将解析后的 Color 转回十六进制字符串展示,同时验证往返一致性。

isDark() 的使用提示:使用前请确认 isLight() 的行为是否符合你的预期。在当前版本中 isLight() 返回 isDark() 的值,两者结果相同。如果需要"判断是否为浅色",建议使用 !color.isDark() 而非 color.isLight()(见 FAQ Q3)。


六、运行与验证

以下为 demo 工程的真机实测记录。仓库中 example 的 Bundle Name 为 com.example.ohos_example_scaffold,签名配置使用 DevEco Studio 自动签名。

设备项
机型OpenHarmony 真机
设备 ID4UQ9K25508013016
系统版本OpenHarmony 6.1.1.120
API 版本24
架构arm64

HarmonyOS 技术点:FlutterAbility 与 EntryAbility

鸿蒙 Flutter 应用的入口 Ability 需要继承 FlutterAbility(由 @ohos/flutter_ohos 提供),而非标准的 UIAbilityFlutterAbility 内部封装了 FlutterEngine 的初始化、Surface 注册、路由管理等逻辑。宿主工程的 EntryAbility 只需重写 configureFlutterEngine 方法,在其中调用 GeneratedPluginRegistrant.registerWith(flutterEngine) 即可完成所有原生插件的注册:

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
  }
}

这一设计与 Android 端的 FlutterActivity.configureFlutterEngine 高度对称。

6.1 验证一:构建与安装

构建 hap 后安装到真机并启动:

# 构建 hap(debug,含签名)
flutter build hap --debug

# 安装到真机
hdc install entry-default-signed.hap

# 启动 demo
hdc shell aa start -b com.example.ohos_example_scaffold -a EntryAbility

构建成功产出 entry-default-signed.hap,安装成功(install bundle successfully),启动成功(start ability successfully)。

通过 hilog 确认 Flutter 渲染引擎正常工作:

hdc shell "timeout 5 hilog | grep -E 'render_service|flutter'"

实测日志输出:

render_service/skia: RSSurfaceRenderNodeDrawable::OnDraw name:oh_flutter_1Surface

Flutter 引擎 surface oh_flutter_1Surface 正常绘制,demo 页面(交互式解析面板 + 格式色板 + 工具方法演示)完整渲染。

在这里插入图片描述

6.2 验证二:交互式颜色解析

在输入框中输入不同的十六进制字符串,观察色块和解析结果的变化:

输入解析结果色块显示往返一致
#FF8800Color(0xffff8800)橙色
FF0000Color(0xffff0000)红色
#00FF0080Color(0xff00ff00) → 半透明绿色半透明绿色
#ffa500Color(0xffffa500)橙色(小写输入)
空字符串(开启默认色)Color(0xff9e9e9e)灰色N/A
空字符串(关闭默认色)ArgumentError错误提示N/A

每种输入格式的解析结果均正确,色块实时更新,toHex(includeAlpha: true) 往返一致性验证通过。

在这里插入图片描述

6.3 验证三:Color 工具方法

demo 界面下方的工具方法区域展示五种颜色(白、黑、灰、琥珀色、深蓝)的 isDark()getBrightness()getLuminance() 结果:

颜色toHex()isDark()getBrightness()getLuminance()
白色 #ffffff#fffffffalse255.01.000
黑色 #000000#000000true0.00.000
灰色 #808080#808080true128.00.180
琥珀色 #ffc107#ffc107false203.30.489
深蓝 #003366#003366true42.90.032

所有亮度判断和计算结果正确,与 ITU-R BT.601 加权公式和 Flutter computeLuminance() 的预期值一致。

实测结论:

验证点结果
应用启动,Flutter 页面正常渲染通过
getColorFromHex() 正确解析 6 位、8 位、无 # 前缀、小写输入通过
toHex(includeAlpha: true) 往返一致性验证通过
isDark() / getBrightness() / getLuminance() 亮度判断通过
空输入 + defaultColor 兜底通过
空输入无 defaultColorArgumentError通过
全程无需申请任何权限通过
全程纯 Dart 实现,Dart 层零改动通过

在这里插入图片描述

在这里插入图片描述


七、工作原理

整个调用链路如下:

Dart: getColorFromHex('#FF8800')
  → 纯 Dart: 字符串归一化 + int.parse + Color 构造
    → 返回 Color 对象(无原生通道调用)

Dart: color.toHex(includeAlpha: true)
  → 纯 Dart: 逐通道 toRadixString(16) + padLeft + 拼接
    → 返回十六进制字符串(无原生通道调用)

Dart: color.isDark()
  → 纯 Dart: getBrightness() < 128.0
    → 返回 bool(无原生通道调用)
鸿蒙侧插件注册链路(仅模板契约,Dart 层从未调用):

EntryAbility.configureFlutterEngine()
  → GeneratedPluginRegistrant.registerWith(flutterEngine)
    → flutterEngine.getPlugins().add(new ColorFromHexPlugin())
      → ColorFromHexPlugin.onAttachedToEngine(binding)
        → new MethodChannel(messenger, "color_from_hex")
          → setMethodCallHandler(this)

HarmonyOS 技术点:纯 Dart 插件与原生插件的区别

Flutter 插件分为两类:纯 Dart 插件(所有逻辑在 Dart 侧完成,不调用任何原生平台 API)和原生插件(需要通过 MethodChannel/BasicMessageChannel 调用原生平台 API)。color_from_hex 属于纯 Dart 插件——颜色解析、转换和亮度计算全部在 Dart 侧用 int.parsetoRadixString 等标准库方法完成,不涉及任何系统级 API。鸿蒙侧的 ArkTS 插件类 ColorFromHexPlugin 仅复刻了 Android/iOS 的模板契约(getPlatformVersion),是 flutter create 生成的骨架代码,Dart 层从未调用该通道。因此,即使在鸿蒙端不注册任何原生插件,color_from_hex 的核心功能也能正常工作——原生插件类只是保证注册链路完整性,与 Android/iOS 行为一致。

7.1 Dart 层实现解析

库的 Dart 层包含两个文件,逐段解析如下。

get_color_from_hex.dart:十六进制解析函数

import 'package:flutter/material.dart';

Color getColorFromHex(String hexColor, {Color? defaultColor}) {
  if (hexColor.isEmpty) {
    if (defaultColor != null) {
      return defaultColor;
    } else {
      throw ArgumentError('Can not parse provided hex $hexColor');
    }
  }
  hexColor = hexColor.toUpperCase().replaceAll('#', '');
  if (hexColor.length == 6) {
    hexColor = 'FF$hexColor';
  }
  return Color(int.parse(hexColor, radix: 16));
}

这段代码已在 5.2 节详细解析,核心逻辑为:空输入兜底 → 格式归一化(大写 + 去 #)→ 6 位补全 FF → int.parse 解析为 Color。

to_hex_color.dart:Color 扩展方法

extension Hex on Color {
  String toHex({bool leadingHashSign = true, bool includeAlpha = false}) =>
      '${leadingHashSign ? '#' : ''}'
      '${includeAlpha ? alpha.toRadixString(16).padLeft(2, '0') : ''}'
      '${red.toRadixString(16).padLeft(2, '0')}'
      '${green.toRadixString(16).padLeft(2, '0')}'
      '${blue.toRadixString(16).padLeft(2, '0')}';

  bool isDark() => getBrightness() < 128.0;

  bool isLight() => isDark();

  double getBrightness() => (red * 299 + green * 587 + blue * 114) / 1000;

  double getLuminance() => computeLuminance();
}

这段代码定义了 Hex 扩展,为 Flutter 的 Color 类添加了五个方法。toHex() 逐通道转十六进制并拼接;getBrightness() 使用 ITU-R BT.601 加权公式计算感知亮度;isDark() 判断亮度是否低于 128;getLuminance() 委托给 Flutter 引擎的 computeLuminance() 方法。

color_from_hex.dart:统一导出

export 'get_color_from_hex.dart';
export 'to_hex_color.dart';

通过 export 将两个文件统一导出,用户只需 import 'package:color_from_hex/color_from_hex.dart' 即可使用全部 API。

7.2 鸿蒙侧 ArkTS 插件实现

虽然核心功能是纯 Dart 实现,但为保证插件注册链路完整性(与 Android/iOS 行为一致),鸿蒙侧仍提供了 ArkTS 插件类。逐段解析如下。

第一段:导入与类声明

import {
  FlutterPlugin,
  FlutterPluginBinding,
  MethodCall,
  MethodCallHandler,
  MethodChannel,
  MethodResult,
} from '@ohos/flutter_ohos';
import { deviceInfo } from '@kit.BasicServicesKit';

export default class ColorFromHexPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;

这段代码从 @ohos/flutter_ohos 导入 Flutter 鸿蒙适配层的插件接口类型,从 @kit.BasicServicesKit 导入 deviceInfo(用于 getPlatformVersion 返回系统版本信息)。ColorFromHexPlugin 类实现 FlutterPlugin(生命周期管理)和 MethodCallHandler(方法调用处理)两个接口。

HarmonyOS 技术点:@kit.BasicServicesKitdeviceInfo

@kit.BasicServicesKit 是鸿蒙系统提供的基础服务工具包,其中包含 deviceInfo 模块,提供设备信息查询能力。deviceInfo.displayVersion 返回系统的显示版本号(如 HarmonyOS 6.1.1.120),语义上最接近 Android 的 Build.VERSION.RELEASE 和 iOS 的 UIDevice.current.systemVersion。适配过程中,先查询 SDK 的 .d.ts 类型声明文件确认字段名,而非凭记忆编写——这避免了 deviceInfo.version 等不存在字段的编译错误。

第二段:通道注册与生命周期管理

getUniqueClassName(): string {
  return "ColorFromHexPlugin"
}

onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(binding.getBinaryMessenger(), "color_from_hex");
  this.channel.setMethodCallHandler(this)
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  if (this.channel != null) {
    this.channel.setMethodCallHandler(null)
    this.channel = null
  }
}

getUniqueClassName() 返回 "ColorFromHexPlugin",需与 pubspec.yaml 中的 pluginClass 配置一致。onAttachedToEngine 在引擎加载插件时创建名为 "color_from_hex" 的 MethodChannel(与 Android/iOS 通道名一致)并设置方法调用处理器。onDetachedFromEngine 在引擎卸载插件时清理引用。

第三段:方法分发与实现

onMethodCall(call: MethodCall, result: MethodResult): void {
  try {
    if (call.method == "getPlatformVersion") {
      result.success("OpenHarmony " + deviceInfo.displayVersion)
    } else {
      result.notImplemented()
    }
  } catch (err) {
    result.error("ColorFromHexPluginError", (err as Error).message, null)
  }
}

onMethodCall 是方法调用的入口。当前仅处理 "getPlatformVersion" 方法,返回 "OpenHarmony " + deviceInfo.displayVersion(如 "OpenHarmony HarmonyOS 6.1.1.120")。未知方法调用 result.notImplemented() 回复。所有逻辑包裹在 try/catch 中,异常时通过 result.error() 回传错误码和消息,不静默失败。

通道契约三端对照

契约项Android (Kotlin)iOS (Swift)OHOS (ArkTS)
通道名color_from_hexcolor_from_hexcolor_from_hex
方法名getPlatformVersiongetPlatformVersiongetPlatformVersion
返回值"Android ${Build.VERSION.RELEASE}""iOS " + UIDevice.current.systemVersion"OpenHarmony " + deviceInfo.displayVersion
未知方法result.notImplemented()FlutterMethodNotImplementedresult.notImplemented()

三端通道契约完全一致,仅返回值的前缀和系统版本来源不同。Dart 层从未调用 getPlatformVersion——它是 flutter create 生成的模板骨架方法,保留它是为了与 Android/iOS 的行为保持一致。

7.3 插件注册机制

鸿蒙侧的插件注册是自动完成的。Flutter 鸿蒙适配层在构建时扫描 pubspec.yaml 中的 ohos: pluginClass 配置,自动生成 GeneratedPluginRegistrant.ets 文件:

import { FlutterEngine, Log } from '@ohos/flutter_ohos';
import IntegrationTestPlugin from 'integration_test';
import ColorFromHexPlugin from 'color_from_hex';

export class GeneratedPluginRegistrant {
  static registerWith(flutterEngine: FlutterEngine) {
    try {
      flutterEngine.getPlugins()?.add(new IntegrationTestPlugin());
      flutterEngine.getPlugins()?.add(new ColorFromHexPlugin());
    } catch (e) {
      Log.e(TAG, "Tried to register plugins with FlutterEngine failed.");
    }
  }
}

注册链路为:EntryAbility.configureFlutterEngine()GeneratedPluginRegistrant.registerWith(flutterEngine)flutterEngine.getPlugins().add(new ColorFromHexPlugin())ColorFromHexPlugin.onAttachedToEngine(binding) → 创建 MethodChannel 并设置处理器。

HarmonyOS 技术点:HAR 模块

color_from_hex 的鸿蒙侧原生代码以 HAR(Harmony Archive)模块形式打包,类似于 Android 的 AAR。HAR 模块在 module.json5 中声明 "type": "har",可以被宿主工程通过 oh-package.json5 依赖引入。Flutter 的 ohos 适配层会自动在宿主工程的 GeneratedPluginRegistrant 中注册插件类,无需手动编写注册代码。


八、常见问题

Q1:color_from_hex 在鸿蒙端是否需要原生插件?

核心功能(十六进制解析、Color 转十六进制、亮度判断)全部是纯 Dart 实现,不依赖任何原生平台 API,鸿蒙端开箱即用。鸿蒙侧的 ArkTS 插件类 ColorFromHexPlugin 仅复刻了 Android/iOS 的模板契约(通道名 color_from_hex、方法 getPlatformVersion),是 flutter create 生成的骨架代码,Dart 层从未调用该通道。原生插件的存在仅为保证插件注册链路完整性,与 Android/iOS 行为一致。

Q2:6 位和 8 位十六进制字符串的解析规则是什么?

6 位十六进制(如 FF8800)被解析为不透明颜色:自动在前补 FF 变成 FFFF8800,即 Alpha=FF(完全不透明)。8 位十六进制(如 FF880080)按 ARGB 顺序解析:前两位 FF 为 Alpha,中间两位 88 为 Red,后四位 0080 为 Green 和 Blue。注意 Flutter 的 Color 构造函数使用 ARGB 顺序(0xAARRGGBB),与 CSS 的 #RRGGBBAA 顺序不同——CSS 中透明度在最后两位,而 Flutter 中透明度在最前两位。

在这里插入图片描述

Q3:isLight() 返回值与预期不符是怎么回事?

在当前版本中,isLight() 的实现是 bool isLight() => isDark(),即直接返回 isDark() 的结果。这意味着当颜色为深色时,isDark()isLight() 都返回 true。这可能是一个已知的设计取舍或代码 bug。如果需要判断"是否为浅色",建议使用 !color.isDark() 而非 color.isLight(),或直接检查 color.getBrightness() >= 128.0

Q4:空输入时会发生什么?

getColorFromHex('') 在空输入时检查 defaultColor 参数:如果提供了 defaultColor,返回该默认色;如果未提供,抛出 ArgumentError('Can not parse provided hex ')。业务层可以通过 defaultColor 参数优雅地处理空输入场景,例如 getColorFromHex(serverColor, defaultColor: Colors.blue)

在这里插入图片描述

Q5:为什么 toHex() 默认不输出透明度?

toHex()includeAlpha 参数默认为 false,即默认输出 6 位十六进制字符串(#rrggbb),不含透明度通道。这是因为大多数颜色展示场景(如 CSS 颜色值、设计稿标注)使用 6 位格式。如果需要输出 8 位含透明度的格式(#aarrggbb),设置 includeAlpha: true。往返一致性验证时需使用 toHex(includeAlpha: true) 再传给 getColorFromHex(),否则 6 位输出会丢失透明度信息。

Q6:鸿蒙端的 getPlatformVersion 返回什么?

鸿蒙侧的 ColorFromHexPlugingetPlatformVersion 方法中返回 "OpenHarmony " + deviceInfo.displayVersiondeviceInfo 来自 @kit.BasicServicesKitdisplayVersion 是系统的显示版本号。这一返回值与 Android 的 "Android ${Build.VERSION.RELEASE}" 和 iOS 的 "iOS " + UIDevice.current.systemVersion 语义对齐,但 Dart 层从未调用此方法——它是模板骨架的一部分。


九、结语

回顾一下:在 pubspec.yaml 中以 git TAG 引入 color_from_hex,调用 getColorFromHex('#FF8800') 即可将十六进制字符串解析为 Color 对象,调用 color.toHex() 可反向转换,isDark() / getBrightness() 提供亮度判断。核心功能全部是纯 Dart 实现,跨平台共享,鸿蒙端开箱即用,Dart 层零改动。鸿蒙侧的 ArkTS 插件类仅复刻模板契约保证注册链路完整,与 Android/iOS 行为一致。已在 OpenHarmony 6.1.1.120 真机(API 24 / arm64)完整实测,四种输入格式解析、往返一致性、亮度判断全部通过。

总结对比

接口功能实现方式参数返回值
getColorFromHex()十六进制字符串 → Color纯 DarthexColor: String, defaultColor: Color?Color
toHex()Color → 十六进制字符串纯 Dart 扩展leadingHashSign: bool, includeAlpha: boolString
isDark()判断是否为深色纯 Dart 扩展bool
getBrightness()感知亮度 (0.0~255.0)纯 Dart 扩展double
getLuminance()相对亮度 (0.0~1.0)Flutter 引擎double
输入格式示例解析行为Alpha
6 位 + ##FF8800去前缀,补 FFFF(不透明)
6 位无 #FF8800直接补 FFFF(不透明)
8 位 + ##FF880080去前缀,直接解析FF
小写#ffa500大写归一化FF(不透明)
空字符串''返回 defaultColor 或抛异常N/A

核心要点:纯 Dart 实现,零权限,零原生调用,鸿蒙端开箱即用,Dart 层零改动

使用中发现任何问题,欢迎到配套仓库提 Issue,也欢迎发 PR 共建。

相关链接

欢迎加入 CPF-Flutter 鸿蒙社区,社区入口、环境搭建指南和本文相关链接统一放在这里:

Logo

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

更多推荐