Flutter-自定义画板

news/2024/9/24 23:28:33/

效果

功能

  • 支持绘制线、圆、矩形,支持拓展
  • 支持撤回上一步
  • 支持清空画板
  • 支持自定义画笔颜色,宽度

实现

定义绘制类型

/// 类型
enum ShapeType {//线line,//圆circle,//矩形rectangle,//拓展
}

定义绘制抽象类

import 'dart:ui';/// 绘制抽象类
abstract class Shape {void draw(Canvas canvas,List<Offset> points,Paint paint,);
}

实现线、圆、矩形绘制

/// 绘制圆
class CircleShape implements Shape {@overridevoid draw(Canvas canvas, List<Offset> points, Paint paint) {if (points.length >= 2) {double radius = (points[points.length - 1] - points[1]).distance / 2;paint.style = PaintingStyle.stroke;canvas.drawCircle(points[0], radius, paint);}}
}/// 绘制线
class LineShape implements Shape {@overridevoid draw(Canvas canvas, List<Offset> points, Paint paint) {for (int i = 0; i < points.length - 1; i++) {canvas.drawLine(points[i], points[i + 1], paint);}}
}/// 绘制方
class RectangleShape implements Shape {@overridevoid draw(Canvas canvas, List<Offset> points, Paint paint) {if (points.length >= 2) {final rect = Rect.fromPoints(points[0], points[points.length - 1]);paint.style = PaintingStyle.stroke;canvas.drawRect(rect, paint);}}
}

定义工厂类 factory

/// 根据绘制类型返回具体绘制对象
Shape getShape(ShapeType type) {switch (type) {case ShapeType.line:return LineShape();case ShapeType.circle:return CircleShape();case ShapeType.rectangle:return RectangleShape();}
}

定义笔画参数对象

/// 笔画参数对象
class DrawingStroke {Color color;double width;List<Offset> points;ShapeType type;DrawingStroke({this.color = Colors.black,this.width = 2.0,this.points = const [],this.type = ShapeType.line,});
}

定义绘制控制器

/// 绘制控制器
class DrawingController {final _strokes = <DrawingStroke>[];final _listeners = <VoidCallback>[];// 所有绘制笔画数据List<DrawingStroke> get strokes => List.unmodifiable(_strokes);// 画笔颜色Color selectedColor = Colors.black;// 画笔宽度double strokeWidth = 2.0;// 绘制类型ShapeType selectedType = ShapeType.line;// 开始绘制void startDrawing(Offset point) {_strokes.add(DrawingStroke(color: selectedColor,width: strokeWidth,points: [point],type: selectedType,));_notifyListeners();}// 正在绘制void updateDrawing(Offset point) {if (_strokes.isNotEmpty) {_strokes.last.points.add(point);_notifyListeners();}}// 结束当前笔画绘制void endDrawing() {_notifyListeners();}// 撤回一笔void undo() {if (_strokes.isNotEmpty) {_strokes.removeLast();_notifyListeners();}}// 清空数据void clear() {_strokes.clear();_notifyListeners();}// 设置画笔颜色void setColor(Color color) {selectedColor = color;_notifyListeners();}// 设置画笔宽度void setStrokeWidth(double width) {strokeWidth = width;_notifyListeners();}// 设置绘制类型void setDrawingType(ShapeType type) {selectedType = type;_notifyListeners();}void _notifyListeners() {for (var listener in _listeners) {listener();}}void addListener(VoidCallback listener) {_listeners.add(listener);}void removeListener(VoidCallback listener) {_listeners.remove(listener);}
}

定义画板类DrawingBoard

class DrawingBoard extends StatefulWidget {final DrawingController controller;const DrawingBoard({Key? key, required this.controller}) : super(key: key);@overrideState<StatefulWidget> createState() => DrawingBoardState();
}class DrawingBoardState extends State<DrawingBoard> {@overridevoid initState() {super.initState();widget.controller.addListener(_updateState);}void _updateState() {setState(() {});}@overridevoid dispose() {super.dispose();widget.controller.removeListener(_updateState);}@overrideWidget build(BuildContext context) {return LayoutBuilder(builder: (context, size) {return SizedBox(width: size.maxWidth,height: size.maxHeight,child: GestureDetector(onPanStart: (details) {widget.controller.startDrawing(details.localPosition);},onPanUpdate: (details) {widget.controller.updateDrawing(details.localPosition);},onPanEnd: (_) {widget.controller.endDrawing();},child: CustomPaint(painter: DrawingPainter(strokes: widget.controller.strokes),size: Size.infinite,),),);});}
}class DrawingPainter extends CustomPainter {final Paint drawPaint = Paint();DrawingPainter({required this.strokes});List<DrawingStroke> strokes;@overridevoid paint(Canvas canvas, Size size) {for (var stroke in strokes) {drawPaint..color = stroke.color..strokeCap = StrokeCap.round..strokeWidth = stroke.width;Shape shape = getShape(stroke.type);shape.draw(canvas, stroke.points, drawPaint);}}@overridebool shouldRepaint(covariant CustomPainter oldDelegate) {return false;}
}
使用画板
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_xy/xydemo/drawingboard/drawing/type.dart';
import 'package:flutter_xy/xydemo/drawingboard/drawing/view.dart';import 'drawing/controller.dart';class DrawingPage extends StatefulWidget {const DrawingPage({Key? key}) : super(key: key);@overrideDrawingPageState createState() => DrawingPageState();
}class DrawingPageState extends State<DrawingPage> {final _controller = DrawingController();@overridevoid initState() {super.initState();SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft,DeviceOrientation.landscapeRight,]);}@overridevoid dispose() {super.dispose();SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp,DeviceOrientation.portraitDown,]);}@overrideWidget build(BuildContext context) {return Scaffold(body: SafeArea(child: Row(children: [SizedBox(width: 120,child: ListView(scrollDirection: Axis.vertical,padding: const EdgeInsets.symmetric(horizontal: 20),children: [const SizedBox(width: 10),_buildText("操作"),const SizedBox(width: 10),_buildButton('Undo', () => _controller.undo()),const SizedBox(width: 10),_buildButton('Clear', () => _controller.clear()),const SizedBox(width: 10),_buildText("画笔颜色"),const SizedBox(width: 10),_buildColorButton(Colors.red),const SizedBox(width: 10),_buildColorButton(Colors.blue),const SizedBox(width: 10),_buildText("画笔宽度"),const SizedBox(width: 10),_buildStrokeWidthButton(2.0),const SizedBox(width: 10),_buildStrokeWidthButton(5.0),const SizedBox(width: 10),_buildText("画笔类型"),const SizedBox(width: 10),_buildTypeButton(ShapeType.line, '线'),const SizedBox(width: 10),_buildTypeButton(ShapeType.circle, '圆'),const SizedBox(width: 10),_buildTypeButton(ShapeType.rectangle, '方'),],),),Expanded(child: Column(children: [Expanded(child: DrawingBoard(controller: _controller,),),],),),],),),);}Widget _buildText(String text) {return Text(text,style: const TextStyle(fontSize: 12,fontWeight: FontWeight.w600,),);}Widget _buildButton(String text, VoidCallback onPressed) {return ElevatedButton(onPressed: onPressed,child: Text(text),);}Widget _buildColorButton(Color color) {return ElevatedButton(onPressed: () => _controller.setColor(color),style: ElevatedButton.styleFrom(primary: color),child: const SizedBox(width: 30, height: 30),);}Widget _buildStrokeWidthButton(double width) {return ElevatedButton(onPressed: () => _controller.setStrokeWidth(width),child: Text(width.toString()),);}Widget _buildTypeButton(ShapeType type, String label) {return ElevatedButton(onPressed: () => _controller.setDrawingType(type),child: Text(label),);}
}

运行效果如下图:

详情见 github.com/yixiaolunhui/flutter_xy


http://www.ppmy.cn/news/1435236.html

相关文章

RustGUI学习(iced)之小部件(一):如何使用按钮和文本标签部件

前言 本专栏是学习Rust的GUI库iced的合集&#xff0c;将介绍iced涉及的各个小部件分别介绍&#xff0c;最后会汇总为一个总的程序。 iced是RustGUI中比较强大的一个&#xff0c;目前处于发展中&#xff08;即版本可能会改变&#xff09;&#xff0c;本专栏基于版本0.12.1. 概述…

2024年宝鸡市国家级、省级、市级科技企业孵化器申报奖励补贴标准及申报条件

一、宝鸡市各区县科技企业孵化器申报奖励 &#xff08;wotao&#xff0c;专注项目申报、专利商标版权DL12年&#xff01;zi询看主页&#xff09; 宝鸡市&#xff1a;对新认定的国家、省、市级科技企业孵化器&#xff0c;建成并投入良性运行后&#xff0c;分别给予30万元、20万…

pytorch-解决过拟合之regularization

目录 1.解决过拟合的方法2. regularization2. regularization分类3. pytorch L2 regularization4. 自实现L1 regularization5. 完整代码 1.解决过拟合的方法 更多的数据降低模型复杂度 regularizationDropout数据处理早停止 2. regularization 以二分类的cross entropy为例&…

更新至2022年上市公司数字化转型数据合集(四份数据合集)

更新至2022年上市公司数字化转型数据合集&#xff08;四份数据合集&#xff09; 一、2000-2022年上市公司数字化转型数据&#xff08;年报词频、文本统计&#xff09; 二、2007-2022年上市公司数字化转型数据&#xff08;年报和管理层讨论&#xff09;&#xff08;含原始数据…

【Java那些年系列-启航篇 03】JDK、JRE和JVM之间是什么关系?

作者名称&#xff1a;纸飞机-暖阳 作者简介&#xff1a;专注于Java和大数据领域&#xff0c;致力于探索技术的边界&#xff0c;分享前沿的实践和洞见 文章专栏&#xff1a;Java那些年专栏 专栏介绍&#xff1a;本专栏涵盖了 Java SE从基础语法到面向对象编程&#xff0c;从异常…

Flink CDC详解

文章目录 Flink CDC一 CDC简介1.1 CDC定义1.2 CDC应用场景1.3 CDC实现机制1.4 开源CDC工具对比 二 Flink CDC简介2.1 Flink CDC介绍2.2 Flink CDC Connector(连接器)2.3 Flink CDC && Flink版本2.4 Flink CDC特点 三 Flink CDC发展3.1 发展历程3.2 背景Dynamic Table &…

Hive——DDL(Data Definition Language)数据定义语句用法详解

1.数据库操作 1.1创建数据库 CREATE DATABASE [IF NOT EXISTS] database_name [COMMENT database_comment] [LOCATION hdfs_path] [WITH DBPROPERTIES (property_nameproperty_value, ...)];IF NOT EXISTS&#xff1a;可选参数&#xff0c;表示如果数据库已经存在&#xff0c…

vue - 路由守卫

Vue路由守卫是一种机制&#xff0c;用于在导航过程中对路由进行监控和控制。Vue Router提供了三种类型的路由守卫&#xff1a; 1. 全局前置守卫&#xff1a;router.beforeEach 2. 全局解析守卫&#xff1a;router.beforeResolve 3. 全局后置守卫&#xff1a;router.afterEac…