Flutter 框架跨平台鸿蒙开发 - 打造专业级颜色选择器,色轮/HSV/RGB三模式切换
使用Canvas绘制色相环和渐变面板手势处理:通过角度计算实现色轮交互颜色模型转换:RGB与HSV的双向同步亮度感知:根据颜色亮度自动调整UI元素颜色颜色选择器是一个很好的CustomPainter练习项目,涉及到数学计算(角度、坐标)、颜色理论(RGB/HSV)和手势交互等多个知识点。欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net。
·
Flutter实战:打造专业级颜色选择器,色轮/HSV/RGB三模式切换
颜色选择器是设计工具和图形应用的核心组件。本文将用Flutter实现一款功能完善的颜色选择器,支持色轮、HSV、RGB三种选色模式,提供多种颜色格式输出,并支持颜色收藏功能。
效果预览图


功能特性
- 🎨 色轮选择:直观的色相环,拖动选取颜色
- 🌈 HSV模式:色相/饱和度/明度独立调节
- 🔴🟢🔵 RGB模式:红/绿/蓝三通道滑块
- 📋 多格式输出:HEX、RGB、RGBA、HSV、Flutter Color
- 💾 颜色收藏:保存常用颜色,快速调用
- 📝 HEX输入:直接输入十六进制颜色值
颜色模型基础
RGB 与 HSV
| 模型 | 维度 | 范围 | 特点 |
|---|---|---|---|
| RGB | 红、绿、蓝 | 0-255 | 计算机原生,加色混合 |
| HSV | 色相、饱和度、明度 | H:0-360°, S/V:0-100% | 更符合人类直觉 |
| HEX | 十六进制 | #000000-#FFFFFF | 网页常用格式 |
颜色转换
Flutter 内置了 HSVColor 类,可以方便地进行 RGB 和 HSV 之间的转换:
// RGB -> HSV
final hsv = HSVColor.fromColor(color);
double hue = hsv.hue; // 0-360
double saturation = hsv.saturation; // 0-1
double value = hsv.value; // 0-1
// HSV -> RGB
final color = HSVColor.fromAHSV(1, hue, saturation, value).toColor();
int red = color.red;
int green = color.green;
int blue = color.blue;
// RGB -> HEX
String hex = '#${color.value.toRadixString(16).substring(2).toUpperCase()}';
核心实现
状态管理
颜色选择器需要同步维护 RGB 和 HSV 两套值:
class _ColorPickerAppState extends State<ColorPickerApp> {
Color _currentColor = Colors.blue;
// HSV值
double _hue = 210;
double _saturation = 1.0;
double _value = 1.0;
// RGB值
int _red = 0;
int _green = 0;
int _blue = 255;
// 从Color更新所有值
void _updateFromColor(Color color) {
final hsv = HSVColor.fromColor(color);
setState(() {
_currentColor = color;
_hue = hsv.hue;
_saturation = hsv.saturation;
_value = hsv.value;
_red = color.red;
_green = color.green;
_blue = color.blue;
});
}
// 从HSV更新
void _updateFromHSV() {
setState(() {
_currentColor = HSVColor.fromAHSV(1, _hue, _saturation, _value).toColor();
_red = _currentColor.red;
_green = _currentColor.green;
_blue = _currentColor.blue;
});
}
// 从RGB更新
void _updateFromRGB() {
setState(() {
_currentColor = Color.fromRGBO(_red, _green, _blue, 1);
final hsv = HSVColor.fromColor(_currentColor);
_hue = hsv.hue;
_saturation = hsv.saturation;
_value = hsv.value;
});
}
}
色相环绘制
使用 CustomPainter 绘制色相环,通过绘制360个弧形片段实现渐变效果:
class ColorWheelPainter extends CustomPainter {
final double selectedHue;
ColorWheelPainter(this.selectedHue);
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
// 绘制360个色相片段
for (int i = 0; i < 360; i++) {
final paint = Paint()
..color = HSVColor.fromAHSV(1, i.toDouble(), 1, 1).toColor()
..style = PaintingStyle.stroke
..strokeWidth = 30;
final startAngle = (i - 90) * pi / 180; // 从顶部开始
final sweepAngle = pi / 180; // 1度
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius - 15),
startAngle,
sweepAngle,
false,
paint,
);
}
// 绘制选中指示器
final indicatorAngle = (selectedHue - 90) * pi / 180;
final indicatorX = center.dx + (radius - 15) * cos(indicatorAngle);
final indicatorY = center.dy + (radius - 15) * sin(indicatorAngle);
// 白色外圈
canvas.drawCircle(
Offset(indicatorX, indicatorY),
12,
Paint()..color = Colors.white,
);
// 黑色边框
canvas.drawCircle(
Offset(indicatorX, indicatorY),
12,
Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 2,
);
// 当前颜色填充
canvas.drawCircle(
Offset(indicatorX, indicatorY),
8,
Paint()..color = HSVColor.fromAHSV(1, selectedHue, 1, 1).toColor(),
);
}
}
色相环交互
通过计算触摸点相对于圆心的角度来确定色相值:
GestureDetector(
onPanUpdate: (details) {
final center = const Offset(140, 140); // 圆心
final position = details.localPosition - center;
// 计算角度(弧度)
final angle = atan2(position.dy, position.dx);
// 转换为色相值(0-360)
final hue = (angle * 180 / pi + 360) % 360;
setState(() {
_hue = hue;
_updateFromHSV();
});
},
child: CustomPaint(
painter: ColorWheelPainter(_hue),
),
)
饱和度-明度面板
HSV模式下的二维选择面板,水平方向是饱和度,垂直方向是明度:
class SaturationValuePainter extends CustomPainter {
final double hue;
SaturationValuePainter(this.hue);
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
// 饱和度渐变(水平:白色 -> 纯色)
final saturationGradient = LinearGradient(
colors: [
Colors.white,
HSVColor.fromAHSV(1, hue, 1, 1).toColor(),
],
);
// 明度渐变(垂直:透明 -> 黑色)
final valueGradient = const LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black],
);
// 先绘制饱和度
canvas.drawRect(rect, Paint()..shader = saturationGradient.createShader(rect));
// 再叠加明度
canvas.drawRect(rect, Paint()..shader = valueGradient.createShader(rect));
}
}
UI组件实现
颜色预览区
显示当前颜色,并根据亮度自动调整文字颜色:
Widget _buildColorPreview() {
// 计算亮度,决定文字颜色
final luminance = _currentColor.computeLuminance();
final textColor = luminance > 0.5 ? Colors.black : Colors.white;
return Container(
height: 140,
color: _currentColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// HEX值(可点击复制)
GestureDetector(
onTap: () => _copyToClipboard(_hexColor),
child: Text(
_hexColor,
style: TextStyle(
color: textColor,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
// RGB和HSV格式
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('RGB($_red, $_green, $_blue)', style: TextStyle(color: textColor)),
Text('HSV(${_hue.toInt()}°, ${(_saturation*100).toInt()}%, ${(_value*100).toInt()}%)',
style: TextStyle(color: textColor)),
],
),
],
),
);
}
RGB滑块
带颜色标识的RGB通道滑块:
Widget _buildColorSlider(String label, int value, Color color, ValueChanged<int> onChanged) {
return Row(
children: [
// 颜色标识圆点
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
child: Center(
child: Text(label, style: TextStyle(color: Colors.white)),
),
),
// 滑块
Expanded(
child: SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: color,
thumbColor: color,
),
child: Slider(
value: value.toDouble(),
min: 0,
max: 255,
onChanged: (v) => onChanged(v.toInt()),
),
),
),
// 数值显示
Text(value.toString()),
],
);
}
HEX输入框
支持直接输入十六进制颜色值:
Widget _buildHexInput() {
return Row(
children: [
const Text('#', style: TextStyle(fontSize: 20)),
Expanded(
child: TextField(
maxLength: 6,
decoration: InputDecoration(hintText: 'FFFFFF'),
onSubmitted: (value) {
if (value.length == 6) {
try {
final color = Color(int.parse('FF$value', radix: 16));
_updateFromColor(color);
} catch (_) {}
}
},
),
),
],
);
}
颜色格式输出
提供多种常用格式,一键复制:
Widget _buildColorFormats() {
final formats = [
{'name': 'HEX', 'value': _hexColor},
{'name': 'RGB', 'value': 'rgb($_red, $_green, $_blue)'},
{'name': 'RGBA', 'value': 'rgba($_red, $_green, $_blue, 1)'},
{'name': 'HSV', 'value': 'hsv(${_hue.toInt()}, ${(_saturation*100).toInt()}%, ${(_value*100).toInt()}%)'},
{'name': 'Flutter', 'value': 'Color(0xFF${_hexColor.substring(1)})'},
];
return Column(
children: formats.map((f) => Row(
children: [
Text(f['name']!),
Expanded(child: Text(f['value']!)),
IconButton(
icon: Icon(Icons.copy),
onPressed: () => _copyToClipboard(f['value']!),
),
],
)).toList(),
);
}
颜色收藏功能
final List<Color> _savedColors = [];
void _saveColor() {
if (!_savedColors.contains(_currentColor)) {
setState(() {
_savedColors.insert(0, _currentColor);
if (_savedColors.length > 20) _savedColors.removeLast(); // 最多保存20个
});
}
}
Widget _buildSavedColors() {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: _savedColors.length,
itemBuilder: (context, index) {
final color = _savedColors[index];
return GestureDetector(
onTap: () => _updateFromColor(color), // 点击应用
onLongPress: () => setState(() => _savedColors.removeAt(index)), // 长按删除
child: Container(
width: 44,
height: 44,
color: color,
),
);
},
);
}
应用架构
数据流程
扩展建议
- 透明度支持:添加Alpha通道滑块
- 调色板:提供Material/Cupertino等预设调色板
- 颜色历史:记录最近使用的颜色
- 颜色方案:生成互补色、类似色、三角色等
- 图片取色:从图片中提取颜色
- 导出功能:导出为CSS变量、Sass变量等格式
项目结构
lib/
└── main.dart
├── ColorPickerApp # 主应用(Tab切换)
├── _buildColorWheel() # 色轮选择器
├── _buildHSVPicker() # HSV选择器
├── _buildRGBPicker() # RGB选择器
├── ColorWheelPainter # 色相环绘制器
└── SaturationValuePainter # 饱和度-明度面板绘制器
颜色格式对照
| 颜色 | HEX | RGB | HSV |
|---|---|---|---|
| 🔴 红 | #FF0000 | rgb(255,0,0) | hsv(0°,100%,100%) |
| 🟢 绿 | #00FF00 | rgb(0,255,0) | hsv(120°,100%,100%) |
| 🔵 蓝 | #0000FF | rgb(0,0,255) | hsv(240°,100%,100%) |
| 🟡 黄 | #FFFF00 | rgb(255,255,0) | hsv(60°,100%,100%) |
| 🟣 紫 | #800080 | rgb(128,0,128) | hsv(300°,100%,50%) |
| ⚫ 黑 | #000000 | rgb(0,0,0) | hsv(0°,0%,0%) |
| ⚪ 白 | #FFFFFF | rgb(255,255,255) | hsv(0°,0%,100%) |
总结
这个颜色选择器展示了几个关键技术点:
- CustomPainter:使用Canvas绘制色相环和渐变面板
- 手势处理:通过角度计算实现色轮交互
- 颜色模型转换:RGB与HSV的双向同步
- 亮度感知:根据颜色亮度自动调整UI元素颜色
颜色选择器是一个很好的CustomPainter练习项目,涉及到数学计算(角度、坐标)、颜色理论(RGB/HSV)和手势交互等多个知识点。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐

所有评论(0)