Flutter三方库适配OpenHarmony【color_picker】HSL 调色器项目完整实战

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 转换派生状态 getterSliderTheme 局部样式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 技术目标

本文围绕真实源码拆解以下内容:

  1. Flutter 应用入口和粉色 Material 3 主题。
  2. HSL 三个状态字段如何驱动页面。
  3. _currentColor getter 如何把 HSL 转换为 Color
  4. _getColorCode 如何生成 HEX 文本。
  5. _getColorName 如何根据 HSL 粗略命名颜色。
  6. _buildColorInfo 如何复用 HEX、RGB、HSL 信息展示。
  7. 三个 Slider 如何实时更新 HSL 状态。
  8. SliderTheme 如何局部调整滑块外观。
  9. 预设色板如何通过 WrapGestureDetector 实现。
  10. 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 内置的 ColorHSLColor,不需要额外三方库。

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 已经包含 ColorHSLColorSliderCardWrapGestureDetectorSnackBar 等项目所需能力。

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 滑块验证

滑块交互要覆盖:

  1. Hue 从 0 拖到 360,颜色按色相变化。
  2. Saturation 降到 0,颜色变为灰阶。
  3. Lightness 降到 0,颜色接近黑色。
  4. Lightness 升到 1,颜色接近白色。
  5. 拖动过程中 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 输入和主题导出能力,让调色器从演示工具逐步演进为更完整的跨平台颜色工具。

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源:

相关推荐
韩曙亮1 小时前
【Flutter】Flutter 组件 ② ( 组件大小设置 | 固定大小 | 自适应大小 | 填充父容器 | 百分比大小 )
flutter·自适应大小·flutter组件大小·固定大小·填充父容器
G_dou_1 小时前
Flutter三方库适配OpenHarmony【random_number】随机数生成器项目完整实战
flutter·harmonyos
FrameNotWork2 小时前
HarmonyOS 6.1 云应用客户端适配实战(三):触摸输入与坐标映射
华为·harmonyos
●VON2 小时前
鸿蒙Flutter实战:日期选择器与截止日期高亮提醒
android·flutter·华为·harmonyos·鸿蒙
●VON2 小时前
鸿蒙Flutter实战:Material 3种子色亮暗双主题系统
android·flutter·harmonyos
慧海灵舟2 小时前
鸿蒙南向开发教程 Day 3:OpenHarmony 线程管理
华为·harmonyos·写文章,赢小鸿ai
想你依然心痛2 小时前
HarmonyOS 6(API 23)实战:打造“光味智厨“——AI烹饪新体验
人工智能·华为·ar·harmonyos·智能体
灰鲸广告联盟2 小时前
新老用户广告价值不同?差异化策略如何实现收益最大化
android·开发语言·flutter·ios
颜淡慕潇3 小时前
低成本搭建鸿蒙PC运行环境:基于 Docker 的 x86_64 服务器
服务器·docker·harmonyos