Flutter for OpenHarmony 文件转换助手App实战 - 颜色选择器
解析要稳:输入再脏也别让页面崩。输出要友好:用户要的不是一个Color对象,而是能拿去用的 HEX/RGB/HSL 文本。支持#RGB#ARGB短格式。历史记录(最近用过的颜色一排小色块)。取色器(从图片中取色),这在工具类 App 里很受欢迎。欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net。

颜色选择器算是工具类 App 里“看起来简单,做起来细节一堆”的功能:输入格式五花八门、要即时预览、还要能一键复制。
这篇笔记按真实项目的写法,把颜色选择器拆成几块:
- 入口(工具列表):点击后打开弹窗。
- 弹窗(交互容器):输入、预览、信息展示、复制。
- 颜色解析与格式化:把
#RRGGBB/#AARRGGBB转为Color,同时输出 HEX / RGB / HSL。
颜色选择器工具的应用
颜色选择器在UI设计和开发中非常重要。设计师可以使用颜色选择器来选择合适的颜色。开发者可以使用颜色选择器来获取颜色的十六进制值或RGB值。
在工具页面中,颜色选择器工具已经定义在工具列表中:
{'icon': Icons.palette, 'title': '颜色选择器'},
上面这一行只是“展示”,项目里我一般会再加一个稳定的 key(后续做埋点/跳转/排序都用得到)。如果你现在不需要,可以先不加,但建议养成习惯。
点击入口:打开颜色选择器
工具列表的 onTap 不要直接塞一整坨 UI 代码,后面维护会很痛。把入口抽成一个方法,结构更清楚:
void _onToolTap(BuildContext context, String title) {
if (title == '颜色选择器') {
_openColorPicker(context);
return;
}
}
这里的重点是:
- 入口只做分发:根据标题/标识选择打开哪个工具。
- 具体实现下沉:弹窗、状态、解析逻辑都放到
_openColorPicker里。
颜色选择器对话框的实现
当用户点击颜色选择器工具时,会显示一个对话框。弹窗本身只负责“壳”,内容交给一个独立组件,避免方法越写越大:
Future<void> _openColorPicker(BuildContext context) async {
await showDialog<void>(
context: context,
builder: (_) => const _ColorPickerDialog(),
);
}
这样拆开有两个好处:
- 弹窗打开逻辑更干净:以后加埋点、加权限检查都好加。
- UI 可复用:同样的
_ColorPickerDialog未来也能拿去做“编辑主题色”等场景。
接下来是弹窗主体。为了避免引入额外状态管理,这里用 StatefulWidget 就够用。
class _ColorPickerDialog extends StatefulWidget {
const _ColorPickerDialog();
State<_ColorPickerDialog> createState() => _ColorPickerDialogState();
}
这段代码的目的很单纯:让弹窗内部拥有自己的状态(输入内容、解析后的颜色、展示字符串)。
输入框:尽量“宽容”,但要可控
真实使用里,用户经常会粘贴 #fff、0xFF112233、带空格的字符串。我的建议是:
- 先做“清洗”再解析(去空格、统一大小写、移除前缀)。
- 解析失败不要崩,只提示“格式不对”,预览区显示一个默认色即可。
下面是输入控制器和监听更新(放在 _ColorPickerDialogState 里):
final _inputCtrl = TextEditingController(text: "#FF3B30");
Color? _parsed;
void initState() {
super.initState();
_parsed = tryParseColor(_inputCtrl.text);
_inputCtrl.addListener(() {
setState(() {
_parsed = tryParseColor(_inputCtrl.text);
});
});
}
这里我会把 _parsed 允许为 null:
null表示解析失败,UI 侧可以更明确地展示错误状态。- 如果你强行给个默认色,用户可能不知道自己输入有问题(看起来“好像也能用”)。
颜色格式的支持
颜色选择器如果只支持 #RRGGBB,实际会被吐槽。这个版本我至少会做到:
- HEX:
#RRGGBB、#AARRGGBB(顺带兼容0xFFRRGGBB这种粘贴格式)。 - RGB:展示
rgb(r, g, b),方便前端/设计工具互通。 - HSL:对调色更直观,尤其是想要“同色系深浅”的时候。
颜色解析的实现
在Dart中,我们可以使用Color类来处理颜色。首先需要将颜色代码解析为Color对象:
Color? tryParseColor(String raw) {
final input = raw.trim().toUpperCase();
var hex = input;
if (hex.startsWith('0X')) hex = hex.substring(2);
if (hex.startsWith('#')) hex = hex.substring(1);
if (hex.length == 6) {
return Color(int.parse('FF$hex', radix: 16));
}
if (hex.length == 8) {
return Color(int.parse(hex, radix: 16));
}
return null;
}
我这里故意没做 try/catch,因为长度不对就直接 return null 了;如果你还想支持 #RGB / #ARGB 这种短格式,再补一个分支就行。
颜色信息的格式化:别只给 HEX
解析到 Color 之后,下一步是生成一段“可复制的文本”。我会把 HEX / RGB / HSL 一次性拼出来:
String buildColorInfo(Color color) {
final hex = '#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}';
final rgb = 'rgb(${color.red}, ${color.green}, ${color.blue})';
final hsl = HSLColor.fromColor(color);
final hslText =
'hsl(${hsl.hue.toStringAsFixed(0)}, ${(hsl.saturation * 100).toStringAsFixed(0)}%, ${(hsl.lightness * 100).toStringAsFixed(0)}%)';
return 'HEX: $hex\nRGB: $rgb\nHSL: $hslText';
}
这段输出我保留了换行:
- 复制出去更顺手:粘到备注/工单/聊天里可读性更好。
- 统一大写 HEX:团队里经常会约定格式,避免同一种颜色出现两种写法。
预览区:让用户第一眼就知道“有没有解析成功”
预览区我通常会做两件事:
- 解析失败就给一个明显的占位色(比如灰色)。
- 显示边框,避免浅色在白底上“消失”。
Widget buildPreview(Color? color) {
final preview = color ?? const Color(0xFFE0E0E0);
return Container(
height: 96,
decoration: BoxDecoration(
color: preview,
border: Border.all(color: const Color(0xFFDDDDDD)),
borderRadius: BorderRadius.circular(8),
),
);
}
这块看起来只是 UI,但能显著减少用户的“我是不是输入错了”的不确定感。
复制按钮:用系统剪贴板 + 一个轻提示
复制功能别只 Navigator.pop,用户会以为没生效。更常见的做法是:复制成功后给个 SnackBar。
Future<void> copyInfo(BuildContext context, String text) async {
await Clipboard.setData(ClipboardData(text: text));
if (!context.mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已复制到剪贴板')),
);
}
我会专门加一行 context.mounted:
- 避免异步回调时页面已销毁 导致报错。
- 这在弹窗场景里尤其常见(用户点复制后立刻关闭)。
把这些拼起来:弹窗内容的最小闭环
最后把输入、预览、信息展示和复制按钮串起来。注意这里我刻意控制代码长度,只保留核心结构:
Widget build(BuildContext context) {
final info = _parsed == null ? '请输入正确的颜色值' : buildColorInfo(_parsed!);
return AlertDialog(
title: const Text('颜色选择器'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _inputCtrl,
decoration: const InputDecoration(hintText: '输入 #RRGGBB 或 #AARRGGBB'),
),
const SizedBox(height: 12),
buildPreview(_parsed),
const SizedBox(height: 12),
SelectableText(info),
],
),
),
actions: [
TextButton(onPressed: () => Navigator.pop(context), child: const Text('关闭')),
TextButton(
onPressed: _parsed == null ? null : () => copyInfo(context, info),
child: const Text('复制'),
),
],
);
}
这里有几个“体验细节”是我故意加上的:
SelectableText:用户不想点复制时,也能手动选中一段文本。- 复制按钮禁用:解析失败就不给点,减少误操作。
- 最小闭环:输入 -> 预览 -> 信息 -> 复制,一次对话完成。
总结
颜色选择器这个功能,核心其实就两件事:
- 解析要稳:输入再脏也别让页面崩。
- 输出要友好:用户要的不是一个
Color对象,而是能拿去用的 HEX/RGB/HSL 文本。
如果你后面要继续增强,我建议优先做:
- 支持
#RGB/#ARGB短格式。 - 历史记录(最近用过的颜色一排小色块)。
- 取色器(从图片中取色),这在工具类 App 里很受欢迎。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)