This page looks best with JavaScript enabled

Flutter 自定义绘制控件

 ·  ☕ 2 min read

在 Flutter 中自定义 View 有两种方式:

  1. 组合已有控件
  2. 自定义绘制

如何自定义绘制

有两个类做这件事情:

  • CustomPaint :会在绘制阶段提供一个 Canvas 画布
  • CustomPainter : 具体的画笔, 可配置画笔的颜色,路径等
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CustomPaint(
  painter: Sky(),
  child: Center(
    child: Text(
      'Once upon a time...',
      style: const TextStyle(
        fontSize: 40.0,
        fontWeight: FontWeight.w900,
        color: Color(0xFFFFFFFF),
      ),
    ),
  ),
)

当在绘制阶段时, CustomPaint 首先会调用 painter 在画布上进行绘制, 然后再绘制它的 child 控件, child 绘制完成之后会调用 foregroundPainter 进行绘制. 画布的坐标系和 CustomPaint 的坐标系匹配. CustomPaint 有个 Size 属性标识将绘制多大的区域, 绘制时这个 Size 属性将会传递到 CustomPainterpaint 方法中, 具体的绘制就在paint 方法中进行, void paint(Canvas canvas, Size size); 的方法签名中的两个参数:

  • canvas: 用于绘制的画布, 注意: 该画布是应用所有控件都在使用的, 所以通过这个画布其实是可以绘制充满屏幕的内容的
  • size: 表示应该绘制的区域大小, 每次绘制都应该限制在这个区域内, 否则可能会覆盖了其他控件的绘制结果

实例一

绘制一个矩形和圆角:

![](https://image.youcute.cn/005EDc3Bgy1g14ok70rkyj30b40jy75i.jpg)
1
2
3
4
5
6
Widget build(BuildContext context) {
  return CustomPaint(
    painter: ColorPainter(),
    size: Size(300, 200),
  );
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class ColorPainter extends CustomPainter {

  final red = Color.fromRGBO(255, 0, 0, 1);
  final blue = Color.fromRGBO(0, 0, 255, 1);
 
  @override
  void paint(Canvas canvas, Size size) {  
    final paint = Paint();
    final rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
    // 注意这一句
    canvas.clipRect(rect);
    paint.color = blue;
    canvas.drawRect(rect, paint);
    paint.color = red;
    canvas.drawCircle(Offset.zero, size.height, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}
  • 定义了绘制区域大小, 为 CustomPaint 中的 size 属性
1
    final rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
  • 绘制矩形区域
1
    canvas.drawRect(rect, paint);
  • 绘制圆形
    • 圆心偏移量为: 0, 也就是 CustomPaint 的原点
    • 半径为区域的高度
1
    canvas.drawCircle(Offset.zero, size.height, paint);

最需要注意的地方我觉得是 canvas.clipRect(rect); 这句. 这句表示只绘制给定的区域中的内容. 如果不写这句, 效果就是这样:

![](https://image.youcute.cn/005EDc3Bgy1g14okkt75nj30b40jyabf.jpg)

为什么会这样呢 ?

其实这就是 Size 这个参数的重要性, 因为 Canvas 是被所有控件公有的, 如果我们绘制时不指定区域大小, 那在进行一些形状的绘制时就会出现超出区域的问题.

实例二: 绘制一个带波纹的 CardView

Support the author with
alipay QR Code
wechat QR Code

Yang
WRITTEN BY
Yang
Developer