开发工具: 华为云码道

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

color_from_hex 是一个轻量的 Flutter 颜色工具库,提供十六进制颜色字符串与 Color 的双向转换,以及颜色亮度判断能力。本文以 color_from_hex 0.0.3 为例,介绍源码准备、OHOS 工程配置、ArkTS 实现和 example 真机运行。

插件目前支持 ohos 平台,源码位于 GitHub 配套仓库。文中的代码以提交 b655fdac43509a5cde80d4f2e4eea68d3f2336acfeat: adapt color_from_hex to OpenHarmony)为参考。

在这里插入图片描述

OHOS 适配https://atomgit.com/oh-flutter/plugin-Color-FromHex改动当前在 main 分支工作区,发布 TAG 为 0.0.3-ohos-1.0.0-beta.1
在这里插入图片描述


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

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

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

清空后状态(需要授权)

一、插件简介与适配目标

颜色配置是前端开发的常见需求:服务端下发的主题色、运营配置的皮肤色、设计师给到的标注值,通常都是 #RRGGBB#RRGGBBAA 形式的字符串,而 Flutter 渲染层需要的是 Color 对象。color_from_hex 提供的 getColorFromHex 函数负责这个解析过程;配套的 Hex 扩展则提供 toHex() 反向输出,以及 isDark() / isLight() / getBrightness() / getLuminance() 等亮度判断方法。例如,接口返回 #FF8800 时可以直接得到背景色,再根据 isDark() 决定图标用深色还是浅色。

这些能力全部在 Dart 层完成:解析使用 int.parse(hex, radix: 16),亮度使用感知亮度公式,不依赖任何系统能力,也不发起任何方法通道调用。因此 Dart 层在 OpenHarmony 上可以原样运行,适配工作的核心是补齐 ohos 平台工程和一个与 Android/iOS 契约一致的 ArkTS 插件类,保证插件注册链路完整。



二、环境准备

环境搭建参考社区文档:Flutter OH 开发环境搭建,完成 Flutter OH SDK 安装、环境变量和 DevEco Studio 配置。

完成后,在宿主机终端执行以下命令,确认当前选中的是支持 OHOS 的 Flutter 工具链,并能发现目标设备:

flutter --version
flutter doctor -v
hdc list targets

在这里插入图片描述

在这里插入图片描述

编辑用户

工程使用的工具链和 SDK 配置如下:

项目版本或配置用途
Flutter OHOS SDK3.44.9+ohos-0.0.1-canary1Flutter 编译与 OHOS 平台工具链
Flutter 分支0.0.3-ohos-1.0.0-beta.1flutter doctor 显示的分支名
Dart SDK3.12.2Dart 语言与包管理环境
HarmonyOS 开发套件26.0.0(API 26)开发套件版本及对应的 API 级别
compatibleSdkVersion5.1.0(18)当前工程声明的最低兼容版本
插件版本0.0.3pubspec.yaml 中的包版本
原生语言ArkTSHarmonyOS 插件实现
插件产物HAR被应用 entry 模块依赖

2.1 开发套件版本与工程中的 SDK 版本配置

26.0.0(API 26)5.1.0(18) 涉及 HarmonyOS 开发套件版本(API 版本)及其底座 OpenHarmony 的版本号体系。在本文工程中,相关版本的含义与配置方式如下:

  • 26.0.0(API 26) 表示本机 DevEco Studio 与 OpenHarmony SDK 为 API 26(flutter doctor 显示 available api versions has [26:default])。
  • example/ohos/build-profile.json5 的 product 中只声明了 compatibleSdkVersion,未显式写 compileSdkVersion / targetSdkVersion,构建时使用 DevEco Studio 默认的 API 26 SDK。
  • 5.1.0(18) 是本文工程中 compatibleSdkVersion 的属性值,声明最低兼容 API 18。

对应的 product 配置为:

{
  "name": "default",
  "signingConfig": "default",
  "compatibleSdkVersion": "5.1.0(18)",
  "runtimeOS": "HarmonyOS"
}

在这里插入图片描述

这组配置以 API 26 SDK 编译,最低兼容 API 18。本文真机为 OpenHarmony 6.1.1.120(API 24),高于最低兼容门槛。color_from_hex 的核心功能是纯 Dart 实现,不依赖特定系统版本的 API;插件类中的 getPlatformVersion 仅使用 @kit.BasicServicesKitdeviceInfo,属于基础服务,无需额外系统版本要求。


三、从源码仓库开始准备适配工程

3.1 将上游源码同步到 AtomGit

适配已有第三方插件时,先从 Pub 包信息或项目 README 确认上游地址、包名、版本和许可证。同步仓库时保留源码、许可证和提交历史,方便后续跟踪上游更新。

在 AtomGit 网页的新建仓库流程中,找到从已有仓库导入的入口,填写上游 Git 地址,选择自己有写权限的组织或个人空间,设置目标仓库名称后执行导入。完成后检查目标分支、pubspec.yamlLICENSE 和提交历史是否完整,并记录适配起点的提交号。如果上游本身托管在 AtomGit,也可以通过 Fork 获得自己的工作仓库。

本文适配的 color_from_hex 上游托管在 GitHub(DHY-SOLUTIONS/plugin-Color-FromHex),包名 color_from_hex,基线版本 0.0.3,上游仓库 LICENSE 为 GPL-3.0。下面直接使用 GitHub 地址拉取代码;如需同步到 AtomGit 接收仓库,可在导入后再推送适配提交与 TAG。

3.2 将代码拉取到宿主机

在安装了 Flutter OH 和 DevEco Studio 的开发电脑上打开终端,进入准备存放项目的目录,执行:

git clone https://github.com/DHY-SOLUTIONS/plugin-Color-FromHex.git
cd plugin-Color-FromHex
pwd
ls
git remote -v
git status --short --branch
git rev-parse HEAD

在这里插入图片描述

git clone 会创建 plugin-Color-FromHex/ 目录;cd 后的位置就是下文所说的插件仓库根目录,这里应能看到 pubspec.yamllib/example/。Git 仓库名是 plugin-Color-FromHex,Dart 包名是 color_from_hex

需要使用与本文相同的代码版本时,在没有未提交修改的仓库中切换到以下提交:

git switch --detach f0772314efb0b7c7d06a4a0ba7bde51d3ae0cf63

适配其他插件时,使用该插件对应版本的 tag 或 commit 作为分支起点。

在这里插入图片描述

图 1:在宿主机终端输入仓库拉取命令。

3.3 在仓库根目录确认分支与 TAG

接着在 plugin-Color-FromHex/ 根目录确认分支。本仓库沿用上游的 master 分支承载适配改动,并按 版本号-ohos-适配版本 的规则打发布 TAG,便于业务按 Flutter 框架版本选择依赖。本例为:

git branch --show-current
git tag 0.0.3-ohos-1.0.0-beta.1
git tag -l

如果团队约定需要隔离适配改动,也可以按 feat/ohos_库名称_版本号 创建分支;两种方式选其一即可,关键是 TAG 或分支名能在依赖表中唯一对应一个适配版本。

请添加图片描述

图 2:在 plugin-Color-FromHex 仓库根目录确认分支与发布 TAG。

3.4 自动补全 OHOS 适配结构

color_from_hex 仓库已经包含完整的 ohos/ 平台工程,直接运行示例时可以跳过本节;了解结构如何生成,有助于适配其他尚无 ohos/ 目录的插件。

标准的补全方式是在插件根目录执行:

flutter create --template=plugin --platforms=ohos --project-name color_from_hex .
git status --short
git diff -- pubspec.yaml lib example
  • --template=plugin 指定插件模板。
  • --platforms=ohos 指定需要补全的平台。
  • --project-name color_from_hex 使用 Dart 包名,避免把含连字符和大写字母的仓库目录名作为包名。
  • 最后的 . 表示在当前插件目录补全工程,不是另建一层目录。

本文适配时,本机旧版 Xcode(13.2.1)的 xcodebuild 不支持 -skipPackageUpdates 参数,flutter create 直接崩溃。改用 Flutter OHOS fork 自带的插件模板(templates/app_shared/ 下的 plugin/ohos.tmpl)手工复刻,共 7 个文件:ohos/index.etsoh-package.json5hvigorfile.tsBuildProfile.etsbuild-profile.json5module.json5 和 ArkTS 插件类。生成或复刻后,通过 diff 检查 pubspec.yaml 的变化,保留已有 API、其他平台注册项及依赖配置。不同 Flutter OH 版本生成的模板可能略有差异。

如果生成后 example/ohos/ 仍不存在,进入已有示例应用补全平台:

cd example
flutter create --platforms=ohos .
cd ..

请添加图片描述

图 3:在插件根目录输入 OHOS 结构补全命令。

3.5 适配后的项目目录

适配后的关键目录如下:

plugin-Color-FromHex/
├── lib/
│   ├── color_from_hex.dart              # 入口导出
│   ├── get_color_from_hex.dart          # getColorFromHex 解析函数
│   └── to_hex_color.dart                # Hex 扩展(toHex/isDark 等)
├── ohos/
│   ├── index.ets                        # 插件导出
│   ├── oh-package.json5                 # HAR 包信息
│   └── src/main/
│       ├── ets/components/plugin/
│       │   └── ColorFromHexPlugin.ets   # ArkTS 插件类
│       └── module.json5                 # HAR 模块声明
├── example/
│   ├── lib/main.dart                    # 示例页面
│   ├── integration_test/                # 真机集成测试
│   └── ohos/entry/                      # 宿主 entry 工程
├── test/                                # Dart 单元测试
├── docs/                                # 适配博客与真机截图
└── pubspec.yaml

项目根目录如下,其中包含 ohos/example/ohos/,以及 OpenHarmony 中英文说明、变更记录和适配文档:

请添加图片描述

图 4:适配后的 plugin-Color-FromHex 项目根目录。

文件主要职责
lib/color_from_hex.dart入口,导出解析函数与 Hex 扩展
lib/get_color_from_hex.dart十六进制字符串到 Color 的解析规则
lib/to_hex_color.dartColor 转十六进制字符串及亮度判断
ColorFromHexPlugin.ets注册 Flutter 通道,提供模板方法 getPlatformVersion
插件 module.json5声明 HAR 模块(无权限声明)
示例 entry module.json5声明宿主应用 Ability、设备类型和权限场景
example/lib/main.dart演示实时解析、输入格式色板和工具方法卡片

四、Dart 接口与通道分析

OHOS 适配需要先分清"哪些逻辑走 Dart、哪些走通道"。color_from_hexlib/ 目录只有三个纯 Dart 文件,没有任何 MethodChannel 调用;唯一的通道出现在原生插件类里,用于承载 flutter create 模板自带的 getPlatformVersion 方法,Dart 层从未调用。适配其他已有平台的插件时,也应保留其公开接口和其他平台实现。

本例的对应关系如下:

Dart 入口或模型通道协议OHOS 实现应保持的行为
getColorFromHex(hexColor, {defaultColor})无(纯 Dart)无需原生实现空串返回默认色或抛 ArgumentError;6 位输入补 FF 透明度
Color.toHex({leadingHashSign, includeAlpha})无(纯 Dart)无需原生实现输出格式与参数一致,组件顺序为 A、R、G、B
isDark() / isLight() / getBrightness() / getLuminance()无(纯 Dart)无需原生实现亮度公式与 Flutter 渲染语义一致
模板方法(仅插件骨架)color_from_hex / getPlatformVersiondeviceInfo.displayVersion与 Android/iOS 模板契约一致,未知方法 notImplemented()

4.1 跨端架构与调用时序

Flutter 侧和 HarmonyOS 侧之间的分工是:

  1. 核心功能走纯 DartgetColorFromHexHex 扩展只依赖 flutterColor,在 Dart 虚拟机内同步完成,跨平台共享同一份实现;
  2. 通道仅承载模板契约:ArkTS 插件类注册 MethodChannel('color_from_hex'),实现 getPlatformVersion,保证插件注册链路与 Android/iOS 一致,但业务不会用到它。

模板方法,未被业务调用

Flutter 页面

getColorFromHex / Hex 扩展

flutter Color 解析与亮度计算
纯 Dart,无通道

MethodChannel color_from_hex

ArkTS ColorFromHexPlugin

deviceInfo.displayVersion

业务调用 getColorFromHex('#FF8800') 时,结果在 Dart 侧直接返回,不存在异步等待;方法通道只影响插件能否被正确注册。

4.1.1 一次插件方法调用的时序

虽然业务不调用通道,插件注册后的方法分发链路仍然值得核对。以模板方法为例:

deviceInfo(BasicServicesKit) ColorFromHexPlugin.ets Flutter App deviceInfo(BasicServicesKit) ColorFromHexPlugin.ets Flutter App 未知方法走 result.notImplemented() 异常走 result.error("ColorFromHexPluginError") invokeMethod(getPlatformVersion) onMethodCall 分发 读取 displayVersion 版本字符串 result.success("OpenHarmony " + version)

4.2 解析规则:lib/get_color_from_hex.dart

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));
}

各输入形态的解析结果如下:

输入解析过程结果
'#FF8800'去掉 #,6 位补 FFColor(0xFFFF8800),不透明
'ff8800'toUpperCase()与上一行等价,大小写不敏感
'FF880080'8 位,前两位是 alphaColor(0xFF880080),半透明
'' + defaultColor空串直接走兜底返回 defaultColor
'' 无默认色空串直接抛错ArgumentError

8 位输入的组件顺序是 AARRGGBB:前两位是透明度,不是某些平台习惯的 RRGGBBAA。这是接入时最容易踩的语义差异。

4.3 公开 API 与平台接口

lib/to_hex_color.dart 以扩展方式给 Color 增加能力:

extension Hex on Color {
  /// return hex String
  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')}';

  /// Return true if given Color is dark
  bool isDark() => getBrightness() < 128.0;

  /// Return true if given Color is light
  bool isLight() => isDark();

  /// Returns Brightness of give Color
  double getBrightness() => (red * 299 + green * 587 + blue * 114) / 1000;

  /// Returns Luminance of give Color
  double getLuminance() => computeLuminance();
}

各方法的语义如下:

  • toHex():默认输出带 # 的 6 位小写十六进制;includeAlpha: true 时在开头追加 2 位透明度,与解析侧的 AARRGGBB 顺序对应,保证往返一致;
  • isDark() / isLight():以感知亮度 128 为阈值,两个方法互为镜像;
  • getBrightness()(R×299 + G×587 + B×114) / 1000,范围 0.0 ~ 255.0;
  • getLuminance():转发 Flutter Color.computeLuminance(),返回标准相对亮度。

这些方法全部是同步纯函数,接入时不需要初始化,也不需要 await。

4.4 Dart 通道协议分析

4.4.1 通道名称必须两端完全一致

插件类在 ArkTS 侧注册通道:

this.channel = new MethodChannel(binding.getBinaryMessenger(), "color_from_hex");

通道名称属于跨语言协议。Dart 和 ArkTS 任何一端拼写不一致,都会出现"方法未实现"等问题。本库的 Dart 层没有手写通道代码,通道名只在 ArkTS 插件类出现一次,但仍要与 Android(com.dhy.color_from_hex 插件类注册的通道)保持一致。

4.4.2 Dart 层为什么没有通道实现

模板通常生成的 color_from_hex_method_channel.dartcolor_from_hex_platform_interface.dart 在本仓库中不存在,lib/ 只导出两个纯 Dart 文件:

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

这是有意保留的上游形态:核心功能不需要平台通道,删除模板 Dart 文件可以避免暴露从未实现的接口。test/color_from_hex_method_channel_test.dart 中对应的 import 也以注释形式保留。适配其他插件时,如果上游有平台接口文件,则不能照搬本节,应保留平台接口并在 OHOS 上实现。

4.4.3 未实现方法与异常的行为

ArkTS 侧的 onMethodCall 对未知方法返回 notImplemented(),所有分发逻辑包在 try/catch 中:

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)
  }
}

result.error 使用固定错误码 ColorFromHexPluginError,Dart 侧会以 PlatformException 形式收到,不会静默失败。



五、补全 OHOS 原生实现与工程配置

5.1 在 ColorFromHexPlugin.ets 中实现原生能力

color_from_hex 的核心功能在 Dart 层完成,OHOS 原生侧需要补全的是一个与 Android/iOS 契约一致的模板插件类:注册 MethodChannel('color_from_hex')、实现 getPlatformVersion、处理未知方法和异常。这样 Flutter 工具链才能在构建时正确生成注册代码,示例应用也能像 Android/iOS 一样通过 getPlatformVersion 验证插件链路。

ColorFromHexPlugin 实现 FlutterPluginMethodCallHandler

原生插件位于:

ohos/src/main/ets/components/plugin/ColorFromHexPlugin.ets
5.1.1 引入 Flutter 和 HarmonyOS 能力
import {
  FlutterPlugin,
  FlutterPluginBinding,
  MethodCall,
  MethodCallHandler,
  MethodChannel,
  MethodResult,
} from '@ohos/flutter_ohos';
import { deviceInfo } from '@kit.BasicServicesKit';

其中:

  • FlutterPlugin 负责接入 Flutter Engine 生命周期;
  • MethodChannel 接收 Dart 发来的命令;
  • MethodCallHandler / MethodResult 用于方法分发和结果回传;
  • deviceInfo 来自 @kit.BasicServicesKit,提供 displayVersion 等设备信息字段。

没有 EventChannel、传感器或系统能力相关的 import——本插件不订阅任何系统事件。

5.1.2 连接 Flutter Engine
private channel: MethodChannel | null = null;

getUniqueClassName(): string {
  return "ColorFromHexPlugin"
}

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

onAttachedToEngine 在引擎启动、插件注册时被调用:创建通道并设置处理器。getUniqueClassName() 返回值必须与 pubspec.yaml 中的 pluginClass 一致,否则注册器无法加载插件。

5.1.3 处理 MethodChannel 命令
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)
  }
}

方法名和返回值与 Android 模板的 "Android " + Build.VERSION.RELEASE、iOS 模板的 "iOS " + systemVersion 语义对齐。选择 deviceInfo.displayVersion 前,先在 DevEco SDK 的 @ohos.deviceInfo.d.ts 中确认字段名,避免凭记忆写出不存在的属性。

5.1.4 统一错误处理

onMethodCall 外层的 try/catch 把所有异常统一转换成 result.error,错误码为 ColorFromHexPluginError,错误消息取自异常对象。Dart 侧对应收到 PlatformException(code: ColorFromHexPluginError, ...),可以在 catchErrortry/catch 中处理。

color_from_hex 的通道只承载模板方法,异常场景极少触发;但保留统一的错误出口是平台插件的基本素养,也便于后续在原生侧扩展真实能力时复用。

5.1.5 Engine 解绑时释放资源
onDetachedFromEngine(binding: FlutterPluginBinding): void {
  if (this.channel != null) {
    this.channel.setMethodCallHandler(null)
    this.channel = null
  }
}

Flutter Engine 销毁时移除处理器并释放通道引用。相比持有系统监听的插件,这里没有额外的传感器或事件流资源,但清理逻辑不能省略——它保证插件在热重启、引擎重建等场景下不会重复注册处理器。

5.2 声明插件和宿主权限

color_from_hex 不访问传感器、剪贴板、网络等系统能力,无需任何权限。插件 HAR 与示例宿主都保持最小权限声明,这是纯 Dart 库适配的优势之一。

5.2.1 插件 HAR 的权限

插件的 ohos/src/main/module.json5 保持模板原样,没有 requestPermissions 字段:

{
  "module": {
    "name": "color_from_hex",
    "type": "har",
    "deviceTypes": [
      "default",
      "tablet"
    ]
  }
}

接入方不需要为这个库在 HAR 中追加任何权限声明。

5.2.2 应用 entry 的权限

最终安装的是宿主应用。示例工程的 example/ohos/entry/src/main/module.json5 只声明了 Flutter 调试需要的网络权限:

{
  "module": {
    "requestPermissions": [
      {"name": "ohos.permission.INTERNET"}
    ]
  }
}

INTERNET 是 Flutter OHOS 工具链调试运行(如 DevTools、热重载通道)所需的常规权限,与 color_from_hex 的功能无关。业务接入时,如果宿主应用本身没有网络需求,也可以不声明。

权限声明和运行时授权是两个步骤。接入应用时,还需根据目标 SDK 的权限定义处理授权要求;module.json5 中的声明不会自动完成运行时授权。

5.3 注册并导出插件

pubspec.yaml 通过以下配置声明 OHOS 插件类:

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

ohos.pluginClass: ColorFromHexPlugin 与 ArkTS 类中的 getUniqueClassName() 返回值完全一致。

插件的 ohos/index.ets 需要导出实现:

import ColorFromHexPlugin from './src/main/ets/components/plugin/ColorFromHexPlugin';
export default ColorFromHexPlugin;

执行 flutter pub get 和构建后,Flutter 工具会为应用生成插件注册代码。本例生成的 example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets 中包含:

flutterEngine.getPlugins()?.add(new ColorFromHexPlugin());

通常不应手工编辑 GeneratedPluginRegistrant.ets,因为下次构建可能覆盖它。

注册异常的排查步骤见第九节 MissingPluginException

5.4 检查 example 的 OHOS 应用结构

本例的 example/ohos/build-profile.json5products 中设置最低兼容版本。下面是需核对的配置片段,请合并到现有工程;其中 signingConfig: "default" 需要与本机配置的签名名称一致,签名材料保留在本地:

{
  "app": {
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "compatibleSdkVersion": "5.1.0(18)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  },
  "modules": [
    {
      "name": "entry",
      "srcPath": "./entry",
      "targets": [
        {
          "name": "default",
          "applyToProducts": ["default"]
        }
      ]
    }
  ]
}

工程未显式声明 compileSdkVersion / targetSdkVersion,构建时使用本机 DevEco Studio 默认的 API 26 SDK。另外,AppScope/app.json5bundleNamecom.example.ohos_example_scaffold,必须与签名 Profile 绑定的包名一致。

配置后,在 DevEco Studio 中执行一次 Sync Project。如果 entry 模块正常识别,Project 视图中会出现 entry,并能打开 entry 模块的签名配置。


六、补全交付文件并提交适配分支

6.1 除代码外还要补全哪些文件

代码适配完成后,还需要整理安装说明、接口文档、版本记录和开源信息。接收仓库有专用模板时,按其格式填写:

文件应写清楚的内容
README.OpenSource上游名称、源码地址、适配版本或提交、版权及许可证信息;按仓库模板列出第三方依赖
README.md原项目说明、OHOS 支持入口、配套 Demo 和文档链接;保留上游信息
README.OpenHarmony_CN.md简介、Git 安装方式、TAG 版本对应关系、环境约束、权限、接口表、示例、已验证范围和遗留问题
README.OpenHarmony.md与中文说明对应的英文文档
CHANGELOG.OpenHarmony.mdOHOS 新增能力、适配版本、兼容限制与测试范围
LICENSE / NOTICE保留上游许可证;NOTICE 按许可证和原项目要求保留或补充
example/README.md依赖方式、运行目录、签名、操作步骤与效果图;覆盖解析与工具方法演示
pubspec.yamlohos/oh-package.json5核对包名、版本、插件注册、仓库地址、许可证和依赖
.gitignore忽略构建缓存及本机签名材料,不漏提交必要源码和配置

README.OpenSource 记录库本身的来源与版本。本例的包名为 color_from_hex,版本为 0.0.3,上游仓库 LICENSEGPL-3.0。注意本仓库内部存在许可证表述不一致:ohos/oh-package.json5 写的是 Apache-2.0README.OpenHarmony_CN.md 写的是 MIT,交付前应统一改为与 LICENSE 一致的 GPL-3.0,避免合规歧义。Flutter 和 HarmonyOS SDK 版本写入环境说明。

6.2 提交前检查

提交前先完成第八节的插件、example 和真机测试,再从根目录检查改动:

git branch --show-current
git diff --check
git status --short
git diff --stat
git diff

检查 diff 中的接口、平台注册和依赖变化,移除本机路径及签名信息,并使文档中的版本和分支与提交内容一致。

6.3 提交并推送到远程仓库

文档和代码整理完成后,在根目录暂存并提交。文件名按项目实际情况调整:

git add ohos pubspec.yaml example test .gitignore .metadata
git add README.md README.OpenHarmony_CN.md
git add README.OpenHarmony.md CHANGELOG.OpenHarmony.md docs
git diff --cached --check
git diff --cached --stat
git diff --cached
git commit -m "feat: adapt color_from_hex to OpenHarmony"
git remote -v
git branch --show-current
git push origin master
git tag 0.0.3-ohos-1.0.0-beta.1
git push origin 0.0.3-ohos-1.0.0-beta.1

本仓库直接在 master 分支承载适配提交,并用 TAG 0.0.3-ohos-1.0.0-beta.1 标记发布点,README 中的依赖表按 Flutter 框架版本(3.44)指向该 TAG。若团队约定使用适配分支,把 master 替换为 feat/ohos_color_from_hex_0.0.3 即可。

DevEco 可能向 example/ohos/build-profile.json5 写入本机签名配置,需要在提交前从暂存内容中移除。推送后在托管平台发起合并请求,说明上游来源和版本、OHOS 实现范围、依赖及权限、测试环境、操作结果、已知限制,并附 Demo 运行图。目标分支和评审流程以接收仓库要求为准。


七、使用根目录 example 演示接入

仓库自带 example/,可以直接用来调试插件和体验颜色解析效果。

7.1 本地适配时使用路径依赖

当前 example/pubspec.yaml 的依赖是:

dependencies:
  flutter:
    sdk: flutter
  color_from_hex:
    path: ../

../ 相对于 example/pubspec.yaml 指向插件根目录,修改根目录插件后可直接联调。

7.2 通过 AtomGit 引入插件

业务应用引入时,将 color_from_hexpath 配置替换为下面的 Git 依赖。这里固定到本文使用的 TAG:

dependencies:
  flutter:
    sdk: flutter
  color_from_hex:
    git:
      url: https://github.com/DHY-SOLUTIONS/plugin-Color-FromHex.git
      ref: 0.0.3-ohos-1.0.0-beta.1

使用自己的适配版本时,先推送提交,再将 url 改为对应仓库,ref 改为适配分支名或 TAG。正式发布后可固定到 tag 或 commit。

从插件根目录执行:

cd example
flutter pub get
flutter pub deps

检查 example/pubspec.lockcolor_from_hex 的来源为 git,并核对 urlrefresolved-ref。同时检查没有 dependency_overridespubspec_overrides.yaml 将其覆盖回本地依赖,确认应用使用的是 Git 依赖。

7.3 调用接口实现颜色解析

下面的页面展示 hex 输入、实时解析和亮度判断,可用于 example/lib/main.dart。仓库中的完整 Demo 还提供输入格式色板和工具方法卡片。

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

void main() {
  runApp(const MaterialApp(home: HexColorPage()));
}

class HexColorPage extends StatefulWidget {
  const HexColorPage({super.key});

  
  State<HexColorPage> createState() => _HexColorPageState();
}

class _HexColorPageState extends State<HexColorPage> {
  final TextEditingController _controller =
      TextEditingController(text: '#FF8800');
  Color? _parsed;
  String? _error;

  void _parse(String input) {
    setState(() {
      try {
        _parsed = getColorFromHex(input);
        _error = null;
      } catch (e) {
        _parsed = null;
        _error = '$e';
      }
    });
  }

  
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('color_from_hex Demo')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            TextField(
              controller: _controller,
              onChanged: _parse,
              decoration: const InputDecoration(
                labelText: '输入 hex 颜色',
                hintText: '如 #FF8800 / FF8800 / #FF880080',
              ),
            ),
            const SizedBox(height: 16),
            if (_error != null)
              Text(_error!, style: const TextStyle(color: Colors.redAccent))
            else ...[
              Container(
                width: 64,
                height: 64,
                color: _parsed,
              ),
              const SizedBox(height: 8),
              Text('Color(${_parsed.toARGB32()})'),
              Text('toHex(): ${_parsed!.toHex()}'),
              Text('toHex(includeAlpha): ${_parsed!.toHex(includeAlpha: true)}'),
              Text('isDark: ${_parsed!.isDark()}'),
            ],
          ],
        ),
      ),
    );
  }
}

7.4 页面退出时的资源处理

异步回调先检查 mounted,避免页面销毁后继续调用 setStatecolor_from_hex 的 API 都是同步纯函数,没有订阅或原生资源需要清理,dispose 中只需释放页面自己的 TextEditingController 后调用 super.dispose()

如果应用中存在全局主题色解析(例如启动时从服务端配置解析主题),可以在应用级做一次解析并缓存结果,避免在 build 中反复调用字符串解析。


八、验证、构建与鸿蒙设备运行效果

8.1 分别验证插件与 example

从插件仓库根目录执行:

flutter pub get
flutter analyze
flutter test
cd example
flutter pub get
flutter analyze
flutter test

test/ 下的模板测试文件为空壳(void main() {}),功能验证主要依靠 example/integration_test/plugin_integration_test.dart:断言示例页渲染、"支持的输入格式"区块存在,以及输入 #FF0000 后实时解析输出 toHex(): #ff0000往返一致: true 等。集成测试需在真机或模拟器上执行:

cd example
flutter test integration_test

Dart 测试覆盖接口和页面逻辑。color_from_hex 的功能不依赖传感器、权限等设备能力,无需额外的真机能力验证。

8.2 确认设备连接

hdc list targets
flutter devices

设备首次连接电脑时,需要在手机端确认调试授权。列表为空时,检查 USB 连接、调试模式和电脑授权。

8.3 配置签名

真机安装的 HAP 通常需要有效签名。推荐使用 DevEco Studio 为 entry 模块配置自动签名:

  1. 用 DevEco Studio 打开 example/ohos,不是仓库根目录;
  2. 等待工程 Sync 成功,确认 Project 视图中存在 entry 模块;
  3. 打开 File > Project Structure > Signing Configs
  4. default product 选择或生成签名;
  5. 确认设备、应用包名、证书和 Profile 匹配;
  6. 再回到终端执行 Flutter 构建或运行。

签名材料保存在本机,公开仓库中只保留构建所需的通用配置。

8.4 运行示例

以下命令在 example/ 目录执行,将 <device-id> 替换为设备列表中的实际 ID:

flutter run -d <device-id>

也可以先构建 HAP:

flutter build hap --debug

典型产物位于:

example/ohos/entry/build/default/outputs/default/

目录中通常包含已签名和未签名 HAP。真机安装应选择与当前设备匹配的已签名产物。

8.5 在设备上测试颜色解析与工具方法

  1. 打开应用,确认实时解析面板已展示 #FF8800 的默认解析结果;
  2. 在输入框分别输入 FF0000(无 #)、#00FF0080(带透明度)、#ffa500(小写),对照"支持的输入格式"色板卡片确认解析结果一致;
  3. 清空输入框,观察错误提示(ArgumentError 信息);
  4. 打开"空输入时使用默认色(灰色)兜底"开关,再次清空输入,确认返回灰色而不是报错;
  5. 在"Color 工具方法"区域核对 toHex()isDark、亮度与相对亮度展示;
  6. 退出页面再进入,确认状态正常恢复,无崩溃。

8.6 鸿蒙设备运行效果

完成适配后,示例应用可以在 OpenHarmony 设备上完成十六进制颜色解析、反向输出和亮度判断,全部能力与 Android/iOS 共享同一份 Dart 实现。

真机运行效果如下:


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

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

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

清空后状态(需要授权)

color_from_hex 不依赖设备硬件和系统服务,满足最低 API 要求的设备行为一致;截图基于 OpenHarmony 6.1.1.120(API 24)真机。


九、FAQ:适配过程与使用问题

9.1 Missing SDK components

典型错误如下:

Missing SDK components. SDK path: ...,
missing components: toolchains,ets,js,native,previewer.

这个错误发生在 Hvigor 同步阶段。通常需要检查构建工具使用的 SDK 路径、组件是否完整,以及 Hvigor 与 SDK 的版本是否匹配。

处理顺序:

  1. 在 DevEco Studio SDK Manager 中确认 API 26 组件已经下载完整;
  2. 检查 Flutter 和 DevEco Studio 使用的 SDK 路径是否一致;
  3. 避免误用 /Applications/DevEco-Studio.app/Contents/sdk 之类的不完整目录;
  4. 确认 SDK 根目录下存在 toolchainsetsjsnativepreviewer
  5. 执行 flutter config --ohos-sdk <正确路径>
  6. 重新执行 flutter doctor -v 和 DevEco Studio Sync。
为什么连接 API 24 手机仍然会报这个错误?

因为 Sync 和 Compile 首先读取 Mac 本地 SDK。手机 API 版本只在部署、安装和运行时参与兼容判断。即使完全不连接手机,本地 SDK 不完整时也会得到相同错误。

当前工程的 compatibleSdkVersion 是 API 18,因此 API 24 在安装版本门槛上是满足的;color_from_hex 的核心功能为纯 Dart 实现,不依赖系统版本能力,安装后即可使用。

9.2 DevEco Studio 中看不到 entry 模块

插件的 ohos/ 目录是 HAR 模块,可安装应用的 entry 模块位于 example/ohos/entry

请直接使用 DevEco Studio 打开:

plugin-Color-FromHex/example/ohos

如果仍看不到 entry,先解决 SDK Sync 错误,再检查 example/ohos/build-profile.json5modules 是否包含 ./entry。同步失败时,Project Structure 无法正确解析模块,签名界面也可能不显示 entry。

9.3 无法手动签名

签名配置依附于可构建的应用模块和 product。只有 HAR 插件模块、工程 Sync 失败,或者打开了错误目录时,DevEco Studio 都可能无法提供 entry 签名入口。

建议先确认:

  • 打开的是 example/ohos
  • SDK 组件完整并且 Sync 成功;
  • entry 的模块类型为 entry
  • default product 和 target 已正确关联;
  • 当前账号、证书和调试设备状态有效。

9.4 解析结果不符合预期或页面无颜色

按以下顺序检查:

  1. 输入是否符合 #RRGGBB / #RRGGBBAA 格式,# 可以省略,大小写不敏感;
  2. 8 位输入的顺序是否正确——前两位是透明度(AARRGGBB),不是 RRGGBBAA;
  3. 空输入且未提供 defaultColor 时会抛出 ArgumentError,这是预期行为,检查界面是否把错误当成了无结果;
  4. 输入变化后是否调用了 setStateonChanged 中漏掉状态更新会导致色块不刷新;
  5. 热重载后确认运行的是最新代码,必要时热重启。

9.5 MissingPluginException

这通常表示 Dart 通道找不到已注册的原生插件。新增原生插件后需要重新构建应用。从仓库根目录执行:

cd example
flutter clean
flutter pub get
flutter run -d <device-id>

如果仍然出现,检查自动生成的插件注册文件中是否包含 ColorFromHexPlugin,同时核对 pubspec.yamlohos/index.etsoh-package.json5

9.6 在 build 中反复解析是否合适

getColorFromHex 是同步纯函数,重复调用本身没有副作用,但在 build 方法里对同一字符串反复解析没有必要。推荐做法:

  • initState 或输入回调中解析一次,把结果保存在状态变量里;
  • 全局主题色在应用启动时解析一次并缓存;
  • 多个页面共用同一配置色时,由应用级服务统一解析后分发。

9.7 编译成功但安装失败

常见原因包括:

  • HAP 未签名或使用了错误的 Profile;
  • 设备未加入调试设备列表;
  • 包名与签名 Profile 不匹配;
  • 安装包的 compatibleSdkVersion 高于设备 API;
  • 手机上已经安装了使用不同证书签名的同包名应用。

根据安装错误码区分签名、版本和包名冲突,再处理对应配置。

9.8 flutter create 不认识 ohos,或包名不合法

先执行 flutter --version,确认使用的是 OHOS 版工具链,环境配置回到第二节的社区链接核对。包名报错时,确认当前目录包含目标插件的 pubspec.yaml,并显式传入 --project-name color_from_hex;仓库名 plugin-Color-FromHex 含大写字母和连字符,不能直接作为 Dart 包名。本仓库适配时 flutter create 因旧版 Xcode 崩溃,改为手工复刻 fork 自带模板,见 3.4 节。

9.9 Git 依赖提示找不到 TAG 或无权限

先检查 URL 是否指向正确的仓库(本文为 GitHub 上的 plugin-Color-FromHex),再确认 0.0.3-ohos-1.0.0-beta.1 TAG 已推送——TAG 与分支是独立推送的,git push origin master 不会带上 TAG,需要单独执行 git push origin 0.0.3-ohos-1.0.0-beta.1。也可以先用适配提交号 8fb8bd663b74ac34527f082c0558fab7a885fa02 作为 ref。私有仓库还需在本机配置 Git 认证。

9.10 改了本地 ArkTS,Demo 为什么没变化

先检查 example/pubspec.yaml:Git 依赖读取远程提交,不会自动读取本地插件改动。本地联调切回 path: ../;测试远程版本则先提交推送,再更新依赖并核对 pubspec.lockresolved-ref。原生代码变动后停止应用并重新构建运行,不能只做 Dart 热重载。

9.11 8 位颜色解析后透明度不符合预期

检查输入顺序:getColorFromHexAARRGGBB 解析,前两位是 alpha。把 RRGGBBAA 顺序的字符串传入(例如本意是 R=FF、G=88、B=00、A=80 的 FF880080)会得到另一种颜色。解析结果可用 toHex(includeAlpha: true) 回显核对,其输出顺序与解析规则一致;需要 RRGGBBAA 顺序时,先手动调换前两位再传入。


相关链接

Logo

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

更多推荐