19 认识自定义绘制组件
1.从绘制点开始说起
我们先通过绘制点来了解一下 Flutter 中绘制的使用方式。左图中是新建的 Paper
组件作为白板主界面;右图在白板的指定坐标处绘制了四个方形的点:
空白 | 绘制四个点 |
---|---|
 |  |
Paper
组件如下,由于之后需要进行切换画笔颜色、粗细的操作;涉及到界面中状态的变化,所以这里继承自 StatefulWidget
,在状态类的 build
方法中,完成界面的构建逻辑。 PaperAppBar
是单独封装的头部标题组件,和之前类似,就不赘述了;现在主要是想让 body
处设置为可绘制的组件。
class Paper extends StatefulWidget {
const Paper({Key? key}) : super(key: key);
@override
State<Paper> createState() => _PaperState();
}
class _PaperState extends State<Paper> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: PaperAppBar(onClear: _clear,),
body: //TODO
);
}
void _clear() {}
}
我们已经知道, Flutter 中的所有界面呈现,都和 Widget
息息相关,绘制也不例外。在 Flutter 中接触到 Canvas 最常用的方式是 CustomPaint
组件 + CustomPainter
画板组合。下面代码,将 body
设置为 CustomPaint
组件;CustomPaint
在构造时传入 painter
参数是 CustomPainter
的子类。
自定义绘制,就是指继承 CustomPainter
完成绘制逻辑,比如这里的 PaperPainter
画板。另外,我们希望画布的尺寸填充剩余空间,可以将 child
指定为 ConstrainedBox
,并通过 BoxConstraints.expand()
的约束。
body: CustomPaint(
painter: PaperPainter(),
child: ConstrainedBox(constraints: const BoxConstraints.expand()),
),
也就是说,对于自定义绘制来说,最重要的是 CustomPainter
子类代码的实现,这里通过 PaperPainter
来完成绘制逻辑。在 paint
回调中,可以访问到 Canvas 对象和画板的尺寸 Size 。 在该方法中通过调用 Canvas
的相关方法就可以进行绘制。比如这里使用 drawPoints
绘制点集,其中需要传入三个参数,分别是:
- 点的模式
PointMode
: 共三种模式, points、lines、polygon - 点集
List<Offset>
: 点的坐标列表 - 画笔
Paint
: 绘制时的配置参数
class PaperPainter extends CustomPainter{
@override
void paint(Canvas canvas, Size size) {
List<Offset> points = const [
Offset(100,100),
Offset(100,150),
Offset(150,150),
Offset(200,100),
];
Paint paint = Paint();
paint.strokeWidth = 10;
canvas.drawPoints(PointMode.points, points , paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
上面点模式是 PointMode.points
,也就是绘制一个个点。如下是另外两者模式的效果,看起来也很清晰:PointMode.lines
会将点集分为若干对,没对连接成线;PointMode.polygon
会将点依次连接;
PointMode.lines | PointMode.polygon |
---|---|
 |  |
到这里,就通过 Canvas 完成了一个最基础的点集绘制案例,当前代码位置 paper.dart 。接下来,介绍一下 Paint
对象,看看你的画笔有哪些功能。
2. 简单认识画笔的属性
可以先回想一下,我们现实中画画时用的笔有哪些特性:很自然的可以想到: 颜色
、粗细
。 这两个配置项分别对应 strokeWidth 和 color 属性,在绘制之前通过直接 paint 对象设置即可:
Paint paint = Paint();
paint.strokeWidth = 10;
paint.color = Colors.red;
粗细 strokeWidth | 颜色 color |
---|---|
 |  |
之前线间的连接很突兀,可以使用将 strokeCap
设置为 StrokeCap.round
让线编程圆头:
paint.strokeCap = StrokeCap.round;
StrokeCap.butt | StrokeCap.round |
---|---|
 |  |
这里面向新手,对于绘制的知识也点到为止,能满足当前需求即可,不会进行非常系统的介绍。不过感兴趣的可以看我的 《Flutter 绘制指南 - 妙笔生花》 小册,其中对于绘制方方面面都介绍的比较详细。
3. 简单的基础图形绘制
Canvas 中提供了一些基础图形的绘制,比如 圆形
、矩形
、圆角矩形
、椭圆
、圆弧
等,这里简单了解一下。
drawCircle 绘制圆形: 三个入参分别是圆心坐标 Offset、半径 double 、 画笔 Paint。
另外,Paint 默认是填充样式,如下左图会填满内部;可以将 style
设置为 PaintingStyle.stroke
变成线型模式,如下右图:
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
canvas.drawCircle(Offset(100, 100), 50, paint);
paint.style = PaintingStyle.stroke;
canvas.drawCircle(Offset(250, 100), 50, paint);
}
drawRect 绘制矩形:两个入参分别是矩形 Rect、画笔 Paint。 drawRRect 绘制圆角矩形:两个入参分别是矩形 RRect、画笔 Paint。
Paint paint = Paint();
paint.style = PaintingStyle.stroke;
paint.strokeWidth = 2;
// 绘制矩形
Rect rect = Rect.fromCenter(center: Offset(100, 100), width: 100, height: 80);
canvas.drawRect(rect, paint);
// 绘制圆角矩形
paint.style = PaintingStyle.fill;
RRect rrect = RRect.fromRectXY(rect.translate(150, 0), 8, 8);
canvas.drawRRect(rrect, paint);
drawOval 绘制椭圆:两个入参分别是矩形 Rect、画笔 Paint。 drawArc 绘制圆弧:五个入参分别是矩形 RRect、起始弧度 double、扫描弧度 double、是否闭合 bool、画笔 Paint。
Paint paint = Paint();
paint.strokeWidth = 2;
// 绘制椭圆
Rect overRect = Rect.fromCenter(center: Offset(100, 100), width: 100, height: 80);
canvas.drawOval(overRect, paint);
// 绘制圆弧
canvas.drawArc(overRect.translate(150, 0), 0, pi*1.3,true,paint);
4. 本章小结
本章主要介绍了如果在 Flutter 中通过 Canvas 自定义绘制内容。界面就相当于一张白纸、绘制接口方法就相当于画笔,使用已经存在的组件固然简单,但学会自己控制画笔绘制内容可以创造更多的精彩。当然,想要精通绘制也不是一朝一夕可以达成的,但凡工艺技能,都是熟能生巧。
这里只是简单认识了在 Flutter 中使用 Canvas 绘制的方式,接下来将结合手势和绘制,完成在手指在界面上拖拽留下痕迹的绘制效果。