效果
功能
- 支持绘制线、圆、矩形,支持拓展
- 支持撤回上一步
- 支持清空画板
- 支持自定义画笔颜色,宽度
实现
定义绘制类型
/// 类型
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