Flutter 框架跨平台鸿蒙开发 - 像素画板
运行效果图像素画板应用是一款专注于像素艺术创作的绘图类应用,旨在帮助用户在手机上轻松绘制像素风格的艺术作品。应用以粉色为主色调,传递创意与活力的品牌形象。应用涵盖了画笔、橡皮擦、填充、取色器、直线、矩形六大绘图工具,支持8x8到64x64多种画布尺寸,提供30种预设颜色选择。通过直观的触控操作和丰富的绘图功能,帮助用户释放创意,创作独特的像素艺术。});
像素画板应用
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
一、项目概述
运行效果图

1.1 应用简介
像素画板应用是一款专注于像素艺术创作的绘图类应用,旨在帮助用户在手机上轻松绘制像素风格的艺术作品。应用以粉色为主色调,传递创意与活力的品牌形象。
应用涵盖了画笔、橡皮擦、填充、取色器、直线、矩形六大绘图工具,支持8x8到64x64多种画布尺寸,提供30种预设颜色选择。通过直观的触控操作和丰富的绘图功能,帮助用户释放创意,创作独特的像素艺术。
1.2 核心功能
| 功能模块 | 功能描述 | 实现方式 |
|---|---|---|
| 像素绘制 | 在网格上绘制像素 | 触控手势识别 |
| 工具切换 | 六种绘图工具切换 | 工具栏选择 |
| 颜色选择 | 选择绘画颜色 | 调色板+取色器 |
| 画布调整 | 调整画布大小 | 弹窗选择 |
| 撤销重做 | 撤销或重做操作 | 历史记录栈 |
| 导出图片 | 导出PNG图片 | 截图保存 |
1.3 绘图工具
| 序号 | 工具名称 | 功能描述 | 使用场景 |
|---|---|---|---|
| 1 | 画笔 | 绘制单个像素 | 自由绘制 |
| 2 | 橡皮擦 | 擦除像素 | 修改错误 |
| 3 | 填充 | 填充连续区域 | 快速上色 |
| 4 | 取色器 | 从画布取色 | 复用颜色 |
| 5 | 直线 | 绘制直线 | 几何图形 |
| 6 | 矩形 | 绘制矩形框 | 边框绘制 |
1.4 技术栈
| 技术领域 | 技术选型 | 版本要求 |
|---|---|---|
| 开发框架 | Flutter | >= 3.0.0 |
| 编程语言 | Dart | >= 2.17.0 |
| 设计规范 | Material Design 3 | - |
| 绑定方式 | CustomPainter | - |
| 目标平台 | 鸿蒙OS / Web | API 21+ |
1.5 项目结构
lib/
└── main_pixel_art.dart
├── PixelArtApp # 应用入口
├── DrawTool # 绘图工具枚举
├── PixelArtHomePage # 主页面
├── PixelPainter # 像素绘制器
└── _PixelArtHomePageState # 状态管理
二、系统架构
2.1 整体架构图
2.2 类图设计
2.3 页面导航流程
2.4 数据流向图
三、核心模块设计
3.1 数据模型设计
3.1.1 像素矩阵
List<List<Color>> _pixels = [];
// 初始化像素矩阵
_pixels = List.generate(
gridSize,
(_) => List.generate(gridSize, (_) => Colors.transparent),
);
3.1.2 绘图工具枚举 (DrawTool)
enum DrawTool {
pencil('画笔', Icons.edit, '绘制像素'),
eraser('橡皮擦', Icons.cleaning_services, '擦除像素'),
fill('填充', Icons.format_color_fill, '填充区域'),
picker('取色器', Icons.colorize, '从画布取色'),
line('直线', Icons.show_chart, '绘制直线'),
rect('矩形', Icons.crop_square, '绘制矩形');
final String label;
final IconData icon;
final String description;
}
3.1.3 历史记录结构
List<List<List<Color>>> _history = [];
int _historyIndex = -1;
// 保存到历史记录
void _saveToHistory() {
_history = _history.sublist(0, _historyIndex + 1);
_history.add(_pixels.map((row) => List<Color>.from(row)).toList());
_historyIndex++;
if (_history.length > 50) {
_history.removeAt(0);
_historyIndex--;
}
}
3.1.4 画布尺寸分布
3.2 页面结构设计
3.2.1 主页面布局
3.2.2 画布区域结构
3.2.3 调色板结构
3.3 绘图算法设计
3.3.1 填充算法流程
3.3.2 直线绘制算法
3.4 触控处理机制
四、UI设计规范
4.1 配色方案
应用采用粉色为主色调,传递创意与活力的品牌形象:
| 颜色类型 | 色值 | 用途 |
|---|---|---|
| 主色 | #E91E63 (Pink) | 导航、强调元素 |
| 画布背景 | #FFFFFF | 绘图区域 |
| 网格线 | #E0E0E0 | 像素分隔线 |
| 工具栏背景 | #F5F5F5 | 工具栏底色 |
| 选中状态 | PrimaryContainer | 工具选中效果 |
4.2 字体规范
| 元素 | 字号 | 字重 | 颜色 |
|---|---|---|---|
| 页面标题 | 20px | Medium | #000000 |
| 工具提示 | 12px | Regular | #757575 |
| 弹窗标题 | 18px | Medium | #000000 |
| 按钮文字 | 14px | Medium | 主色 |
4.3 组件规范
4.3.1 工具栏
┌─────────────────────────────────────────────────────────────┐
│ [✏️] [🧹] [🪣] [💉] [📏] [▢] ↩️ ↪️ 🗑️ │
│ 画笔 橡皮擦 填充 取色 直线 矩形 撤销 重做 清空 │
└─────────────────────────────────────────────────────────────┘
4.3.2 画布区域
┌─────────────────────────────────────┐
│ ┌─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┐ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ │ 像素网格区域 │ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ ├─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┤ │
│ └─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┘ │
└─────────────────────────────────────┘
4.3.3 调色板
┌─────────────────────────────────────────────────────────────┐
│ ┌────┐ [■][■][■][■][■][■][■][■][■][■][■][■][■][■][■]... │
│ │当前│ 黑 白 红 粉 橙 黄 绿 青 蓝 靛 紫 棕 灰 ... │
│ │颜色│ │
│ └────┘ │
└─────────────────────────────────────────────────────────────┘
五、核心功能实现
5.1 像素绘制实现
void _setPixel(int x, int y, Color color) {
if (x >= 0 && x < _gridSize && y >= 0 && y < _gridSize) {
setState(() {
_pixels[y][x] = color;
});
}
}
5.2 填充算法实现
void _fillArea(int startX, int startY, Color newColor) {
if (startX < 0 || startX >= _gridSize || startY < 0 || startY >= _gridSize) {
return;
}
final targetColor = _pixels[startY][startX];
if (targetColor == newColor) return;
final queue = <Offset>[];
final visited = <String>{};
queue.add(Offset(startX.toDouble(), startY.toDouble()));
while (queue.isNotEmpty) {
final point = queue.removeAt(0);
final x = point.dx.toInt();
final y = point.dy.toInt();
final key = '$x,$y';
if (visited.contains(key)) continue;
if (x < 0 || x >= _gridSize || y < 0 || y >= _gridSize) continue;
if (_pixels[y][x] != targetColor) continue;
visited.add(key);
_pixels[y][x] = newColor;
queue.add(Offset((x + 1).toDouble(), y.toDouble()));
queue.add(Offset((x - 1).toDouble(), y.toDouble()));
queue.add(Offset(x.toDouble(), (y + 1).toDouble()));
queue.add(Offset(x.toDouble(), (y - 1).toDouble()));
}
setState(() {});
}
5.3 直线绘制实现(Bresenham算法)
void _drawLine(int x0, int y0, int x1, int y1, Color color) {
final dx = (x1 - x0).abs();
final dy = (y1 - y0).abs();
final sx = x0 < x1 ? 1 : -1;
final sy = y0 < y1 ? 1 : -1;
var err = dx - dy;
var x = x0;
var y = y0;
while (true) {
if (x >= 0 && x < _gridSize && y >= 0 && y < _gridSize) {
_pixels[y][x] = color;
}
if (x == x1 && y == y1) break;
final e2 = 2 * err;
if (e2 > -dy) {
err -= dy;
x += sx;
}
if (e2 < dx) {
err += dx;
y += sy;
}
}
setState(() {});
}
5.4 矩形绘制实现
void _drawRect(int x0, int y0, int x1, int y1, Color color) {
final minX = x0 < x1 ? x0 : x1;
final maxX = x0 < x1 ? x1 : x0;
final minY = y0 < y1 ? y0 : y1;
final maxY = y0 < y1 ? y1 : y0;
// 绘制上下边
for (var x = minX; x <= maxX; x++) {
if (x >= 0 && x < _gridSize) {
if (minY >= 0 && minY < _gridSize) _pixels[minY][x] = color;
if (maxY >= 0 && maxY < _gridSize) _pixels[maxY][x] = color;
}
}
// 绘制左右边
for (var y = minY; y <= maxY; y++) {
if (y >= 0 && y < _gridSize) {
if (minX >= 0 && minX < _gridSize) _pixels[y][minX] = color;
if (maxX >= 0 && maxX < _gridSize) _pixels[y][maxX] = color;
}
}
setState(() {});
}
5.5 触控处理实现
void _handlePanStart(DragStartDetails details, double cellSize) {
final x = (details.localPosition.dx / cellSize).floor();
final y = (details.localPosition.dy / cellSize).floor();
if (x < 0 || x >= _gridSize || y < 0 || y >= _gridSize) return;
switch (_currentTool) {
case DrawTool.pencil:
_setPixel(x, y, _currentColor);
break;
case DrawTool.eraser:
_setPixel(x, y, Colors.transparent);
break;
case DrawTool.fill:
_fillArea(x, y, _currentColor);
_saveToHistory();
break;
case DrawTool.picker:
setState(() {
_currentColor = _pixels[y][x];
});
break;
case DrawTool.line:
_lineStart = Offset(x.toDouble(), y.toDouble());
break;
case DrawTool.rect:
_rectStart = Offset(x.toDouble(), y.toDouble());
break;
}
}
5.6 历史记录实现
void _undo() {
if (_historyIndex > 0) {
setState(() {
_historyIndex--;
_pixels = _history[_historyIndex]
.map((row) => List<Color>.from(row))
.toList();
});
}
}
void _redo() {
if (_historyIndex < _history.length - 1) {
setState(() {
_historyIndex++;
_pixels = _history[_historyIndex]
.map((row) => List<Color>.from(row))
.toList();
});
}
}
5.7 自定义绘制器实现
class PixelPainter extends CustomPainter {
final List<List<Color>> pixels;
final int gridSize;
final bool showGrid;
PixelPainter({
required this.pixels,
required this.gridSize,
required this.showGrid,
});
void paint(Canvas canvas, Size size) {
final cellSize = size.width / gridSize;
final paint = Paint();
for (var y = 0; y < gridSize; y++) {
for (var x = 0; x < gridSize; x++) {
final color = pixels[y][x];
paint.color = color;
paint.style = PaintingStyle.fill;
canvas.drawRect(
Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize),
paint,
);
if (showGrid) {
paint.color = Colors.grey.shade300;
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 0.5;
canvas.drawRect(
Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize),
paint,
);
}
}
}
}
bool shouldRepaint(covariant PixelPainter oldDelegate) {
return pixels != oldDelegate.pixels ||
gridSize != oldDelegate.gridSize ||
showGrid != oldDelegate.showGrid;
}
}
六、交互设计
6.1 绘图交互流程
6.2 工具切换流程
6.3 颜色选择流程
七、扩展功能规划
7.1 后续版本规划
7.2 功能扩展建议
7.2.1 图层功能
绘图能力增强:
- 多图层支持
- 图层可见性控制
- 图层顺序调整
- 图层合并
7.2.2 动画功能
动画创作支持:
- 帧动画制作
- 洋葱皮预览
- GIF导出
- 帧率调整
7.2.3 社交功能
分享与互动:
- 作品分享
- 评论点赞
- 创作者关注
- 热门推荐
八、注意事项
8.1 开发注意事项
-
坐标计算:触控坐标需要转换为像素网格坐标
-
边界检查:所有像素操作前需检查边界
-
历史记录:每次操作后保存状态,限制最大历史数量
-
性能优化:大面积填充时使用队列而非递归
8.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 绘制位置偏移 | 坐标计算错误 | 检查cellSize计算 |
| 填充不完整 | 边界判断错误 | 检查边界条件 |
| 撤销无效 | 历史未保存 | 确保调用_saveToHistory |
| 网格显示异常 | shouldRepaint错误 | 检查重绘条件 |
8.3 使用提示
🎨 像素画板使用指南 🎨
画笔工具:按住并拖动绘制连续像素。
橡皮擦:按住并拖动擦除像素。
填充工具:点击区域填充连续相同颜色。
取色器:点击画布上的像素获取颜色。
直线工具:点击起点,再点击终点绘制直线。
矩形工具:点击起点,再点击终点绘制矩形框。
九、运行说明
9.1 环境要求
| 环境 | 版本要求 |
|---|---|
| Flutter SDK | >= 3.0.0 |
| Dart SDK | >= 2.17.0 |
| 鸿蒙OS | API 21+ |
9.2 运行命令
# 查看可用设备
flutter devices
# 运行到Web服务器
flutter run -d web-server -t lib/main_pixel_art.dart --web-port 8114
# 运行到鸿蒙设备
flutter run -d 127.0.0.1:5555 lib/main_pixel_art.dart
# 运行到Windows
flutter run -d windows -t lib/main_pixel_art.dart
# 代码分析
flutter analyze lib/main_pixel_art.dart
十、总结
像素画板应用通过简洁直观的界面设计,为用户提供了一个便捷的像素艺术创作平台。应用涵盖了画笔、橡皮擦、填充、取色器、直线、矩形六大绘图工具,支持8x8到64x64多种画布尺寸,提供30种预设颜色选择。
核心功能涵盖像素绘制、工具切换、颜色选择、画布调整、撤销重做、导出图片六大模块。像素绘制通过触控手势识别实现;工具切换提供直观的工具栏选择;颜色选择支持调色板快速选择和取色器从画布取色;画布调整支持多种预设尺寸;撤销重做通过历史记录栈实现;导出图片支持PNG格式输出。
应用采用Material Design 3设计规范,以粉色为主色调,界面简洁活力。通过本应用,希望能够帮助用户释放创意,轻松创作独特的像素艺术作品。
用像素描绘创意,让想象变成艺术
更多推荐



所有评论(0)