Flutter三方库适配OpenHarmony【color_picker】HSL 调色器项目完整实战
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
color_picker 是一个基于 Flutter 的 HSL 调色器项目,核心代码位于 lib/main.dart。项目通过 Hue、Saturation、Lightness 三个滑块实时生成颜色,并展示当前颜色的名称、HEX、RGB、HSL 三种格式。页面还提供 Material 预设色板,点击色块后会反向转换为 HSL 状态,驱动颜色预览卡片、AppBar 背景、信息区和滑块同步更新。
这个项目适合讲解 Flutter 颜色模型在 OpenHarmony 上的适配方式。它覆盖了 HSLColor 与 Color 转换 、派生状态 getter 、SliderTheme 局部样式 、Wrap 自适应色板 、颜色文本格式化 、动态 AppBar 背景 和 Material 3 组件渲染。

图片说明:本文围绕 Flutter 颜色模型、滑块交互和 OpenHarmony 承载工程展开,所有关键代码均来自 color_picker 的真实源码。
一个调色器是否好用,不只取决于颜色预览是否正确,还取决于颜色模型、数值格式、交互反馈和跨端渲染是否一致。
一、项目背景与目标
1.1 项目定位
color_picker 是一个轻量级颜色选择工具。用户可以通过三个滑块调整 HSL 参数,也可以点击预设色块快速切换颜色。页面会实时展示当前颜色的大面积预览、颜色名称、HEX 代码、RGB 文本和 HSL 文本。
当前项目真实支持的功能包括:
- 使用 HSL 模型描述当前颜色。
- 使用 Hue 滑块控制色相,范围 0 到 360。
- 使用 Saturation 滑块控制饱和度,范围 0 到 1。
- 使用 Lightness 滑块控制亮度,范围 0 到 1。
- 使用
HSLColor.fromAHSL生成当前Color。 - 使用顶部颜色卡片预览当前颜色。
- 根据饱和度、亮度和色相生成英文颜色名称。
- 展示 HEX、RGB、HSL 三种颜色文本。
- 使用按钮展示 HEX 复制反馈。
- 使用
Wrap渲染预设色板。 - 点击预设色块后通过
HSLColor.fromColor更新 HSL 状态。 - AppBar 背景随当前颜色变化。
1.2 技术目标
本文围绕真实源码拆解以下内容:
- Flutter 应用入口和粉色 Material 3 主题。
- HSL 三个状态字段如何驱动页面。
_currentColorgetter 如何把 HSL 转换为Color。_getColorCode如何生成 HEX 文本。_getColorName如何根据 HSL 粗略命名颜色。_buildColorInfo如何复用 HEX、RGB、HSL 信息展示。- 三个
Slider如何实时更新 HSL 状态。 SliderTheme如何局部调整滑块外观。- 预设色板如何通过
Wrap和GestureDetector实现。 - OpenHarmony 侧如何验证颜色渲染、滑块、色板和文本展示。
1.3 核心实现速览
| 能力 | 当前实现 | 适配关注点 |
|---|---|---|
| 应用入口 | runApp(const ColorPickerApp()) |
确认首屏加载 |
| 主题 | ColorScheme.fromSeed(seedColor: Colors.pink) |
确认 Material 3 样式 |
| 颜色模型 | HSLColor.fromAHSL |
确认 HSL 到 Color 转换 |
| 颜色预览 | 大尺寸 Container |
确认颜色、圆角、阴影 |
| 颜色名称 | _getColorName() |
确认 HSL 分类逻辑 |
| HEX 文本 | _getColorCode() |
确认十六进制格式 |
| RGB 文本 | _currentColor.r/g/b |
确认显示值来源 |
| HSL 文本 | _hue/_saturation/_lightness |
确认百分比显示 |
| 滑块 | 三个 Slider |
确认拖动和状态刷新 |
| 预设色板 | Wrap + GestureDetector |
确认点击和换行布局 |
二、环境准备与工程结构
2.1 工程结构
项目保持 Flutter 标准结构,同时包含 OpenHarmony 平台工程。
| 文件或目录 | 作用 |
|---|---|
lib/main.dart |
应用入口、颜色状态、格式化、滑块和色板 UI |
pubspec.yaml |
SDK 约束、Flutter 依赖和 Material 图标配置 |
analysis_options.yaml |
Flutter lint 规则 |
test/ |
Flutter 测试目录 |
ohos/ |
OpenHarmony 平台承载工程 |
README.md |
项目说明文件 |
当前业务功能集中在 lib/main.dart,没有引入额外颜色选择器插件。
2.2 依赖配置
项目使用 Dart SDK ^3.9.2,依赖 Flutter SDK。
yaml
environment:
sdk: ^3.9.2
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: true
颜色计算使用 Flutter 内置的 Color 和 HSLColor,不需要额外三方库。
2.3 常用命令
bash
flutter pub get
flutter analyze
flutter test
flutter run
| 命令 | 用途 |
|---|---|
flutter pub get |
获取依赖 |
flutter analyze |
执行静态分析 |
flutter test |
执行测试 |
flutter run |
在目标设备运行 |
OpenHarmony 调试时,还需要结合本地 Flutter OpenHarmony 工具链完成平台构建、安装和设备运行。
三、应用入口与主题配置
3.1 import 依赖
项目只引入 Flutter Material。
dart
import 'package:flutter/material.dart';
material.dart 已经包含 Color、HSLColor、Slider、Card、Wrap、GestureDetector、SnackBar 等项目所需能力。
3.2 main 函数
入口函数启动根组件。
dart
void main() {
runApp(const ColorPickerApp());
}
3.3 ColorPickerApp
根组件创建 MaterialApp。
dart
class ColorPickerApp extends StatelessWidget {
const ColorPickerApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Color Picker',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.pink),
useMaterial3: true,
),
home: const ColorPickerHomePage(title: 'Color Picker'),
);
}
}
这段代码包含三个关键点:
- 应用标题为
Color Picker。 - 使用
Colors.pink作为主题种子色。 - 首页为
ColorPickerHomePage。
四、页面状态设计
4.1 StatefulWidget
调色器需要随着滑块拖动持续刷新颜色,因此页面使用 StatefulWidget。
dart
class ColorPickerHomePage extends StatefulWidget {
const ColorPickerHomePage({super.key, required this.title});
final String title;
@override
State<ColorPickerHomePage> createState() => _ColorPickerHomePageState();
}
4.2 HSL 状态字段
页面状态只保存三个核心值。
dart
class _ColorPickerHomePageState extends State<ColorPickerHomePage> {
double _hue = 0;
double _saturation = 1;
double _lightness = 0.5;
}
| 字段 | 默认值 | 范围 | 含义 |
|---|---|---|---|
_hue |
0 | 0 到 360 | 色相 |
_saturation |
1 | 0 到 1 | 饱和度 |
_lightness |
0.5 | 0 到 1 | 亮度 |
默认值对应高饱和、中等亮度的红色。
4.3 派生当前颜色
当前颜色不单独保存,而是通过 getter 动态计算。
dart
Color get _currentColor => HSLColor.fromAHSL(
1,
_hue,
_saturation,
_lightness,
).toColor();
这种做法避免了 HSL 状态和 Color 状态不一致。只要 HSL 三个字段变化,当前颜色就会重新计算。
派生状态能少存就少存。当前项目只保存 HSL 原始值,
Color、HEX、RGB、HSL 文本和颜色名称都从这三个值推导出来。
五、HSL 到 Color 转换
5.1 HSLColor.fromAHSL
Flutter 提供 HSLColor.fromAHSL 构造 HSL 颜色。
dart
HSLColor.fromAHSL(1, _hue, _saturation, _lightness).toColor()
参数含义如下:
| 参数 | 当前值 | 含义 |
|---|---|---|
| alpha | 1 |
不透明度 |
| hue | _hue |
色相角度 |
| saturation | _saturation |
饱和度 |
| lightness | _lightness |
亮度 |
最后调用 toColor() 转换为 Flutter UI 可直接使用的 Color。
5.2 HSL 模型特点
HSL 比直接调 RGB 更适合做调色器:
- Hue 控制颜色类型,例如红、黄、绿、蓝。
- Saturation 控制颜色鲜艳程度。
- Lightness 控制明暗。
用户调色时,HSL 的三个参数更符合直觉。
5.3 默认颜色推导
默认状态为:
text
hue = 0
saturation = 1
lightness = 0.5
这会生成一个标准红色。页面初始时,大色块和 AppBar 都会呈现红色倾向。
六、颜色代码与名称
6.1 HEX 生成
_getColorCode 将当前颜色转为 HEX。
dart
String _getColorCode(Color color) {
return '#${color.toHexString().substring(2).toUpperCase()}';
}
toHexString() 来自自定义扩展,返回 ARGB 字符串;substring(2) 去掉 Alpha,只保留 RGB 部分。
6.2 ColorExtension
项目定义了 ColorExtension。
dart
extension ColorExtension on Color {
String toHexString() {
return '${(a * 255).toInt().toRadixString(16).padLeft(2, '0')}'
'${(r * 255).toInt().toRadixString(16).padLeft(2, '0')}'
'${(g * 255).toInt().toRadixString(16).padLeft(2, '0')}'
'${(b * 255).toInt().toRadixString(16).padLeft(2, '0')}';
}
}
这里把 alpha、red、green、blue 转为 0 到 255 的整数,再转成十六进制并补齐两位。
6.3 颜色名称
_getColorName 根据 HSL 状态生成粗略英文名称。
dart
String _getColorName() {
if (_saturation < 0.1) {
if (_lightness < 0.2) return 'Black';
if (_lightness > 0.8) return 'White';
return 'Gray';
}
String hueName;
if (_hue < 15 || _hue >= 345) {
hueName = 'Red';
} else if (_hue < 45) {
hueName = 'Orange';
} else if (_hue < 75) {
hueName = 'Yellow';
} else if (_hue < 150) {
hueName = 'Green';
} else if (_hue < 195) {
hueName = 'Cyan';
} else if (_hue < 255) {
hueName = 'Blue';
} else if (_hue < 285) {
hueName = 'Purple';
} else {
hueName = 'Pink';
}
if (_lightness < 0.3) {
hueName = 'Dark $hueName';
} else if (_lightness > 0.7) {
hueName = 'Light $hueName';
}
return hueName;
}
6.4 名称规则表
| 条件 | 名称 |
|---|---|
| 饱和度小于 0.1 且亮度小于 0.2 | Black |
| 饱和度小于 0.1 且亮度大于 0.8 | White |
| 饱和度小于 0.1 | Gray |
| 色相小于 15 或大于等于 345 | Red |
| 色相小于 45 | Orange |
| 色相小于 75 | Yellow |
| 色相小于 150 | Green |
| 色相小于 195 | Cyan |
| 色相小于 255 | Blue |
| 色相小于 285 | Purple |
| 其他 | Pink |
亮度小于 0.3 时加 Dark,亮度大于 0.7 时加 Light。
七、颜色预览卡片
7.1 大色块容器
页面顶部使用一个高度为 200 的大色块展示当前颜色。
dart
Container(
width: double.infinity,
height: 200,
decoration: BoxDecoration(
color: _currentColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: _currentColor.withValues(alpha: 0.5),
blurRadius: 20,
offset: const Offset(0, 8),
),
],
),
)
颜色、阴影和 AppBar 都来自 _currentColor,因此拖动滑块后视觉反馈非常直接。
7.2 颜色名称文字
色块中心显示颜色名称。
dart
Text(
_getColorName(),
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: _lightness > 0.5 ? Colors.black : Colors.white,
),
)
文字颜色根据亮度选择黑色或白色,提升可读性。
7.3 AppBar 背景
AppBar 背景也随当前颜色变化。
dart
appBar: AppBar(
title: Text(widget.title),
backgroundColor: _currentColor.withValues(alpha: 0.8),
)
withValues(alpha: 0.8) 保留当前颜色倾向,同时降低纯色压迫感。
八、颜色信息展示
8.1 信息卡片
颜色信息区使用 Card 包裹。
dart
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildColorInfo('HEX', _getColorCode(_currentColor)),
_buildColorInfo('RGB', 'RGB(...)'),
_buildColorInfo('HSL', 'HSL(...)'),
],
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _copyToClipboard,
icon: const Icon(Icons.copy),
label: const Text('Copy HEX Code'),
),
],
),
),
)
信息卡片集中展示当前颜色的三种常用表示方式。
8.2 _buildColorInfo
重复展示结构被封装为 _buildColorInfo。
dart
Widget _buildColorInfo(String label, String value) {
return Column(
children: [
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)),
const SizedBox(height: 4),
Text(value, style: const TextStyle(fontFamily: 'monospace', fontSize: 12)),
],
);
}
这个方法让 HEX、RGB、HSL 三组信息保持统一样式。
8.3 三种文本格式
dart
_buildColorInfo('HEX', _getColorCode(_currentColor))
_buildColorInfo('RGB', 'RGB(${_currentColor.r.toInt()}, ${_currentColor.g.toInt()}, ${_currentColor.b.toInt()})')
_buildColorInfo('HSL', 'HSL(${_hue.toInt()}, ${(_saturation * 100).toInt()}%, ${(_lightness * 100).toInt()}%)')
| 格式 | 当前代码 | 用途 |
|---|---|---|
| HEX | #RRGGBB |
Web、设计稿、主题色 |
| RGB | RGB(r, g, b) |
通用颜色值 |
| HSL | HSL(h, s%, l%) |
调色参数 |
当前源码中 RGB 文本直接读取 Color.r/g/b 后转整数。该值来自 Flutter 新版颜色通道 API,展示语义需要结合当前 SDK 行为理解。
九、复制反馈逻辑
9.1 按钮入口
复制按钮绑定 _copyToClipboard。
dart
ElevatedButton.icon(
onPressed: _copyToClipboard,
icon: const Icon(Icons.copy),
label: const Text('Copy HEX Code'),
)
9.2 当前实现
当前方法展示 SnackBar。
dart
void _copyToClipboard() {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Color code copied: ${_getColorCode(_currentColor)}')),
);
}
这段代码提供了"已复制"的界面反馈,但当前源码没有调用 Clipboard.setData,因此它并未真正把 HEX 写入系统剪贴板。
9.3 完整剪贴板写法
如果需要实际写入剪贴板,可以引入 package:flutter/services.dart 并调用 Clipboard.setData。
dart
import 'package:flutter/services.dart';
void _copyToClipboard() {
final hex = _getColorCode(_currentColor);
Clipboard.setData(ClipboardData(text: hex));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Color code copied: $hex')),
);
}
OpenHarmony 侧验证复制能力时,应以真实粘贴结果为准,而不仅看 SnackBar 是否出现。
十、滑块交互实现
10.1 SliderTheme
三个滑块都使用同样的局部主题。
dart
SliderTheme(
data: SliderThemeData(
trackHeight: 12,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10),
trackShape: const RoundedRectSliderTrackShape(),
),
child: Slider(
value: _hue,
min: 0,
max: 360,
onChanged: (value) {
setState(() {
_hue = value;
});
},
),
)
trackHeight 增加轨道高度,RoundSliderThumbShape 调整圆形滑块大小。
10.2 Hue 滑块
Hue 控制色相。
dart
Slider(
value: _hue,
min: 0,
max: 360,
onChanged: (value) {
setState(() {
_hue = value;
});
},
)
Hue 的范围是 0 到 360,对应色相环角度。
10.3 Saturation 滑块
Saturation 控制饱和度。
dart
Slider(
value: _saturation,
min: 0,
max: 1,
onChanged: (value) {
setState(() {
_saturation = value;
});
},
)
饱和度越高,颜色越鲜艳;饱和度越低,颜色越接近灰阶。
10.4 Lightness 滑块
Lightness 控制亮度。
dart
Slider(
value: _lightness,
min: 0,
max: 1,
onChanged: (value) {
setState(() {
_lightness = value;
});
},
)
亮度越低越接近黑色,亮度越高越接近白色。
十一、预设色板实现
11.1 预设颜色列表
项目定义了一组 Material 预设颜色。
dart
static const List<Color> _presets = [
Colors.red, Colors.pink, Colors.purple, Colors.deepPurple,
Colors.indigo, Colors.blue, Colors.lightBlue, Colors.cyan,
Colors.teal, Colors.green, Colors.lightGreen, Colors.lime,
Colors.yellow, Colors.amber, Colors.orange, Colors.deepOrange,
Colors.brown, Colors.grey, Colors.blueGrey,
];
这些颜色覆盖红、粉、紫、蓝、青、绿、黄、橙、棕、灰等常见色系。
11.2 Wrap 布局
预设色块使用 Wrap 展示。
dart
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (var preset in _presets)
GestureDetector(
onTap: () {
final hsl = HSLColor.fromColor(preset);
setState(() {
_hue = hsl.hue;
_saturation = hsl.saturation;
_lightness = hsl.lightness;
});
},
child: Container(
width: 50,
height: 50,
),
),
],
)
Wrap 会根据屏幕宽度自动换行,非常适合色块网格。
11.3 色块样式
每个预设色块是 50 x 50 的圆角容器。
dart
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: preset,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.grey.shade300),
),
)
边框能让浅色块在白色背景上更容易被识别。
11.4 反向更新 HSL
点击预设颜色后,项目使用 HSLColor.fromColor 转换为 HSL。
dart
final hsl = HSLColor.fromColor(preset);
setState(() {
_hue = hsl.hue;
_saturation = hsl.saturation;
_lightness = hsl.lightness;
});
这让预设色板和三个滑块保持同一套状态源。
十二、页面布局与滚动
12.1 Scaffold 结构
页面使用 Scaffold 承载 AppBar 和内容。
dart
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: _currentColor.withValues(alpha: 0.8),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
children: [
// 预览、信息、滑块、色板
],
),
),
);
12.2 SingleChildScrollView
内容区使用滚动容器。
dart
SingleChildScrollView(
padding: const EdgeInsets.all(24.0),
child: Column(
children: [],
),
)
调色器内容较多,滚动容器可以保证小屏设备上滑块和预设色板都能访问。
12.3 布局顺序
| 顺序 | 区域 | 作用 |
|---|---|---|
| 1 | 颜色预览卡片 | 大面积展示当前颜色 |
| 2 | 颜色信息卡片 | 展示 HEX、RGB、HSL |
| 3 | HSL 滑块卡片 | 调整色相、饱和度、亮度 |
| 4 | 预设色板 | 快速选择常用颜色 |
这个顺序符合取色器使用路径:先看颜色,再看数值,再调参数,再选预设。
十三、OpenHarmony 适配要点
13.1 基础组件验证
当前项目使用的 Flutter 组件包括:
| 组件 | 作用 | OpenHarmony 关注点 |
|---|---|---|
MaterialApp |
应用根组件 | 首屏加载 |
Scaffold |
页面结构 | AppBar 与 Body |
Container |
颜色预览和色块 | 颜色、圆角、阴影 |
Card |
信息和滑块分组 | 阴影、边距 |
Slider |
HSL 参数控制 | 拖动与刷新 |
SliderTheme |
滑块样式 | 轨道高度、圆形滑块 |
Wrap |
色板换行布局 | 小屏换行 |
GestureDetector |
色块点击 | 点击响应 |
SnackBar |
复制反馈 | 展示位置和内容 |
13.2 颜色渲染验证
OpenHarmony 上应重点观察:
- 顶部大色块是否随滑块实时变化。
- AppBar 背景是否同步当前颜色。
- 阴影颜色是否随当前颜色变化。
- 亮色背景上文字是否切换为黑色。
- 暗色背景上文字是否切换为白色。
13.3 滑块验证
滑块交互要覆盖:
- Hue 从 0 拖到 360,颜色按色相变化。
- Saturation 降到 0,颜色变为灰阶。
- Lightness 降到 0,颜色接近黑色。
- Lightness 升到 1,颜色接近白色。
- 拖动过程中 HEX、RGB、HSL 文本同步刷新。
13.4 预设色板验证
预设色板要确认:
- 19 个色块都能显示。
- 色块在小屏上能自动换行。
- 点击色块后顶部预览颜色变化。
- 点击色块后三个滑块状态同步。
- 点击灰色或蓝灰色后名称规则能返回灰阶相关结果。
OpenHarmony 适配验证要覆盖颜色、文本、滑块、色块和反馈链路。调色器最怕只看静态首屏,不验证动态颜色变化。
十四、测试与验证
14.1 初始页面测试
Widget 测试可以验证首屏结构。
dart
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('color picker shows initial widgets', (tester) async {
await tester.pumpWidget(const ColorPickerApp());
expect(find.text('Color Picker'), findsWidgets);
expect(find.text('HEX'), findsOneWidget);
expect(find.text('RGB'), findsOneWidget);
expect(find.text('HSL'), findsOneWidget);
expect(find.text('Hue'), findsOneWidget);
expect(find.text('Saturation'), findsOneWidget);
expect(find.text('Lightness'), findsOneWidget);
expect(find.text('Preset Colors'), findsOneWidget);
});
}
这类测试不依赖具体颜色,只验证页面结构稳定。
14.2 HEX 格式测试
HEX 生成逻辑可以抽成纯函数测试。
dart
String colorToHex(Color color) {
return '#${color.toHexString().substring(2).toUpperCase()}';
}
测试示例:
dart
void main() {
test('red color hex is FF0000', () {
expect(colorToHex(Colors.red), '#F44336');
});
}
Flutter Material 的 Colors.red 不是纯 #FF0000,而是 Material Red 500。
14.3 HSL 名称测试
颜色名称规则可以围绕边界测试。
dart
String describeGray(double saturation, double lightness) {
if (saturation < 0.1) {
if (lightness < 0.2) return 'Black';
if (lightness > 0.8) return 'White';
return 'Gray';
}
return 'Color';
}
测试示例:
dart
void main() {
test('low saturation and low lightness is black', () {
expect(describeGray(0.05, 0.1), 'Black');
});
test('low saturation and high lightness is white', () {
expect(describeGray(0.05, 0.9), 'White');
});
}
14.4 手工验证矩阵
| 场景 | 操作 | 预期 |
|---|---|---|
| 首次打开 | 启动应用 | 显示 Color Picker 页面和红色预览 |
| 拖动 Hue | 改变色相 | 预览颜色变化 |
| 拖动 Saturation | 降低饱和度 | 颜色逐步变灰 |
| 拖动 Lightness | 改变亮度 | 颜色变暗或变亮 |
| 点击预设色 | 点击蓝色块 | 预览和滑块同步到蓝色 |
| 颜色文本 | 调整任意滑块 | HEX/RGB/HSL 同步刷新 |
| 复制反馈 | 点击 Copy HEX Code | 显示包含 HEX 的 SnackBar |
十五、常见问题与优化建议
15.1 为什么使用 HSL 而不是 RGB
RGB 更接近屏幕显示实现,HSL 更接近用户调色习惯。用户通常想表达"换个色相""降低饱和度""调亮一点",这正是 HSL 的三个参数。
dart
HSLColor.fromAHSL(1, _hue, _saturation, _lightness).toColor()
15.2 为什么当前颜色用 getter
_currentColor 是 HSL 的派生结果。
dart
Color get _currentColor => HSLColor.fromAHSL(1, _hue, _saturation, _lightness).toColor();
使用 getter 可以保证当前颜色始终由最新 HSL 状态计算得到,避免保存两份状态后出现不一致。
15.3 为什么色板点击要反向转换
预设色块本身是 Color,而页面状态是 HSL。因此点击色块后需要用 HSLColor.fromColor 反向转换。
dart
final hsl = HSLColor.fromColor(preset);
这样滑块、颜色预览和文本展示都能同步。
15.4 如何实现真正复制 HEX
当前源码只显示 SnackBar。要实际复制 HEX,需要使用 Clipboard。
dart
import 'package:flutter/services.dart';
Clipboard.setData(ClipboardData(text: _getColorCode(_currentColor)));
复制完成后再显示 SnackBar,用户体验会更完整。
15.5 如何显示 0 到 255 的 RGB
如果当前 Flutter SDK 的 Color.r/g/b 返回归一化通道值,可以乘以 255 再取整。
dart
final red = (_currentColor.r * 255).round();
final green = (_currentColor.g * 255).round();
final blue = (_currentColor.b * 255).round();
然后展示为:
dart
'RGB($red, $green, $blue)'
这样更符合常见 RGB 文本习惯。
15.6 如何增加透明度控制
当前 Alpha 固定为 1。可以增加透明度滑块。
dart
double _alpha = 1;
Color get currentColor => HSLColor.fromAHSL(
_alpha,
_hue,
_saturation,
_lightness,
).toColor();
透明度加入后,HEX 也可以扩展为 ARGB 或 RGBA 文本。
十六、工程扩展方向
16.1 抽取颜色工具函数
可以把颜色格式化逻辑抽到独立工具类。
dart
class ColorFormatter {
static String toHex(Color color) {
return '#${color.toHexString().substring(2).toUpperCase()}';
}
}
这样页面代码可以更聚焦 UI。
16.2 建模颜色状态
HSL 状态可以建模为对象。
dart
class HslSelection {
final double hue;
final double saturation;
final double lightness;
const HslSelection({
required this.hue,
required this.saturation,
required this.lightness,
});
}
模型化后,历史记录、收藏颜色和撤销操作都更容易实现。
16.3 增加最近颜色
可以记录最近选择的颜色。
dart
final List<Color> recentColors = [];
void addRecentColor(Color color) {
recentColors.insert(0, color);
if (recentColors.length > 12) {
recentColors.removeLast();
}
}
最近颜色适合设计工具、主题编辑器和低代码配置页面。
16.4 增加收藏色板
用户可以把常用颜色加入收藏。
dart
class FavoriteColor {
final String hex;
final String name;
const FavoriteColor({
required this.hex,
required this.name,
});
}
收藏色板可以结合本地存储,让调色器更接近完整工具。
总结
color_picker 是一个结构清晰的 Flutter HSL 调色器案例。它只保存 _hue、_saturation、_lightness 三个核心状态,通过 _currentColor 动态生成当前颜色,再派生出颜色名称、HEX、RGB、HSL 文本、AppBar 背景、色块阴影和预览文字颜色。预设色板则通过 HSLColor.fromColor 反向更新 HSL 状态,让色板点击和滑块调色共享同一套状态模型。
从 OpenHarmony 适配角度看,这个项目适合验证 Flutter 颜色渲染、滑块拖动、动态背景、阴影、Wrap 换行、GestureDetector 点击和 SnackBar 反馈。由于没有复杂插件依赖,排查路径也比较直接:颜色不对看 HSL 转换,文本不对看格式化逻辑,滑块不动看 setState,色板不同步看 HSLColor.fromColor。
掌握这个项目后,可以继续扩展透明度控制、真实剪贴板复制、最近颜色、收藏色板、RGB 输入、HEX 输入和主题导出能力,让调色器从演示工具逐步演进为更完整的跨平台颜色工具。
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!
相关资源: