Flutter---简单画板应用

效果图

实现逻辑

Dart 复制代码
1.二维列表存储多笔画,一维列表存储当前笔画

2.Listener手势监听

3.创建绘画类

数据存储结构

Dart 复制代码
// 二维列表:存储多笔画
List<List<Offset>> strokes = [];  // 所有完成的笔画
// 例如:strokes = [
//   [p1, p2, p3, ...],  // 第一笔
//   [p4, p5, p6, ...],  // 第二笔
//   [p7, p8, p9, ...]   // 第三笔
// ]

// 一维列表:存储当前笔画
List<Offset>? currentStroke;  // 正在画的笔画

Listener手势监听

Dart 复制代码
Listener(
  onPointerDown: (event) { ... },  // 手指按下
  onPointerMove: (event) { ... },  // 手指移动
  onPointerUp: (event) { ... },    // 手指抬起
)

完整的状态变化示例

Dart 复制代码
// 初始状态
strokes = []                // 空画板
currentStroke = null        // 没有正在画的笔画

// 第一笔:按下
currentStroke = [p1]        // 记录起点

// 第一笔:移动
currentStroke = [p1, p2]    // 添加点
currentStroke = [p1, p2, p3] // 继续添加

// 第一笔:抬起
strokes = [[p1, p2, p3]]    // 保存到历史
currentStroke = null        // 清空当前

// 第二笔:按下
currentStroke = [p4]        // 新的一笔

// 最终状态
strokes = [                 // 两笔画
  [p1, p2, p3],            // 第一笔
  [p4, p5, p6]             // 第二笔
]

绘制原理图解

Dart 复制代码
点列表:[p1, p2, p3, p4, p5]
绘制过程:
p1 ------ p2 ------ p3 ------ p4 ------ p5
 ①     ②     ③     ④
画线①:p1到p2
画线②:p2到p3
画线③:p3到p4
画线④:p4到p5

执行流程

Dart 复制代码
用户触摸屏幕 → onPointerDown 触发

创建新笔画 → 初始化 currentStroke

手指移动 → onPointerMove 连续触发,添加点

实时显示 → 每次 setState 触发 CustomPaint 重绘

手指抬起 → onPointerUp 触发,保存笔画

点击清除 → 清空所有数据,重置画板

代码实例

Dart 复制代码
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // 存储多个笔画,每个笔画是一个点列表
  List<List<Offset>> strokes = [];
  // 当前正在绘制的笔画
  List<Offset>? currentStroke;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('画板'),
        actions: [
          //清除按钮
          IconButton(
            icon: const Icon(Icons.clear),
            onPressed: () {
              setState(() {
                strokes.clear();
                currentStroke = null;
              });
            },
          ),
        ],
      ),
      body: Center(
        child: Listener(
          onPointerDown: (event) {
            setState(() {
              // 开始新的一笔
              currentStroke = [event.localPosition];
            });
          },
          onPointerMove: (event) {
            setState(() {
              // 添加到当前笔画
              currentStroke?.add(event.localPosition);
            });
          },
          onPointerUp: (event) {
            setState(() {
              // 完成当前笔画,添加到笔画列表
              if (currentStroke != null && currentStroke!.length > 1) {
                strokes.add(currentStroke!);
              }
              currentStroke = null;
            });
          },
          child: Container( //画板
            width: 300,
            height: 300,
            color: Colors.grey[200],
            child: CustomPaint(
              painter: DrawingPainter(
                strokes: strokes,
                currentStroke: currentStroke,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class DrawingPainter extends CustomPainter {

  final List<List<Offset>> strokes;  // 所有完成的笔画
  final List<Offset>? currentStroke;  // 当前正在画的笔画

  DrawingPainter({required this.strokes, this.currentStroke});

  @override
  void paint(Canvas canvas, Size size) {
    //配置画笔
    final paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 3.0
      ..strokeCap = StrokeCap.round //圆头端点
      ..style = PaintingStyle.stroke;

    // 绘制所有已完成的笔画
    for (var stroke in strokes) {
      _drawStroke(canvas, stroke, paint);
    }

    // 绘制当前笔画(如果有)
    if (currentStroke != null) {
      _drawStroke(canvas, currentStroke!, paint);
    }
  }

  void _drawStroke(Canvas canvas, List<Offset> stroke, Paint paint) {
    if (stroke.isEmpty) return;

    //情况1:一个点,画一个点
    if (stroke.length == 1) {
      canvas.drawCircle(stroke.first, 2, paint);  //参数:圆心位置、圆半径、画笔样式
      return;
    }

    // 情况2:绘制线条
    for (int i = 0; i < stroke.length - 1; i++) { //把笔画中的相邻点两两连接起来,形成连续的线条
      canvas.drawLine(stroke[i], stroke[i + 1], paint);//参数:点1,点2,画笔样式
    }
  }

  @override
  bool shouldRepaint(covariant DrawingPainter oldDelegate) {
    return oldDelegate.strokes != strokes ||
        oldDelegate.currentStroke != currentStroke;
  }
}
相关推荐
早點睡3901 小时前
Flutter for OpenHarmony:三方库实战irondash_engine_context 引擎上下文详解
flutter
一只程序熊1 小时前
uniappx richtext img 图片无法显示
linux·服务器·数据库
女王大人万岁1 小时前
Golang实战Eclipse Paho MQTT库:MQTT通信全解析
服务器·开发语言·后端·golang
Stewie121382 小时前
企业高性能web服务器——Nginx
服务器·前端·nginx
麦聪聊数据2 小时前
数据流通的最后一公里:SQL2API 在企业数据市场中的履约架构实践
数据库·sql·低代码·微服务·架构
林开落L7 小时前
解决云服务器内存不足:2 分钟搞定 Ubuntu swap 交换区配置(新手友好版)
运维·服务器·ubuntu·swap交换区
加农炮手Jinx7 小时前
Flutter for OpenHarmony: Flutter 三方库 icon_font_generator 自动化将 SVG 图标集转化为字体文件(鸿蒙矢量资源全自动管理)
运维·flutter·华为·自动化·harmonyos·devops
Anastasiozzzz10 小时前
MySQL深分页问题与优化思路
数据库·mysql
松叶似针10 小时前
Flutter三方库适配OpenHarmony【doc_text】— Dart 层架构与 Platform Interface 模式解析
flutter·harmonyos