Flutter中CustomPainter的shouldRepaint()方法是如何工作的?
先搞懂这个方法的作用边界
不用抠官方那些绕口的术语,一句话说清:shouldRepaint 是仅当CustomPaint组件的painter实例被替换时,才会触发的优化判断开关,它不是全局的重绘总开关,也管不到其他场景的重绘逻辑。
这就直接解答你最懵的问题:为啥返回false的时候视图也会重绘?因为父组件setState、系统触发界面刷新这些场景下的重绘,和这个方法没有关系,该重绘还是会重绘。这个方法只是给你多了一个「减少不必要paint调用」的优化机会而已。
什么时候该返回false?
只要你的CustomPainter画的内容是完全固定、没有任何可变参数的,直接返回false就行。
举个最简单的例子:你要画一个永远不会变的红色圆形,不管什么时候新建这个Painter实例,画出来的内容都一模一样:
import 'package:flutter/material.dart'; class StaticRedCirclePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { final paint = Paint()..color = Colors.red; // 画一个占满组件的红色圆形 canvas.drawCircle(size.center(Offset.zero), size.width / 2, paint); } @override bool shouldRepaint(covariant StaticRedCirclePainter oldDelegate) { // 新旧实例画的内容完全一致,不需要重绘 return false; } }
这种场景下返回false,就算父组件频繁setState导致CustomPaint重新build,Flutter对比新旧Painter的时候发现shouldRepaint返回false,就会跳过paint方法的执行,能省不少性能。
什么时候该返回true?
当你的CustomPainter有可变的入参,参数变了绘制内容就会变的时候,你就对比新旧实例的可变参数,只要参数变了就返回true,没变就返回false。
举个例子:你要做一个进度环,进度值是外部传进来的,进度变了环的颜色、弧度都要变:
import 'package:flutter/material.dart'; import 'dart:math'; class ProgressRingPainter extends CustomPainter { // 可变入参:进度值0~1 final double progress; const ProgressRingPainter(this.progress); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 8 // 颜色从红到绿随进度渐变 ..color = Color.lerp(Colors.red, Colors.green, progress)!; // 画对应进度的圆弧 canvas.drawArc( Rect.fromLTWH(0, 0, size.width, size.height), -pi / 2, 2 * pi * progress, false, paint, ); } @override bool shouldRepaint(covariant ProgressRingPainter oldDelegate) { // 只有进度值变了的时候才需要重绘 return oldDelegate.progress != progress; } }
用的时候如果进度从0.3更新到0.5,新的Painter实例和旧的对比进度值不一样,返回true,Flutter就会重新执行paint方法绘制新的进度环;如果进度没变化,就返回false跳过绘制,性能拉满。
常见困惑点统一解答
- 不是说Flutter自己会管重绘吗为啥还要写这个?:Flutter的重绘优化是针对组件树的,而CustomPainter的paint逻辑是自定义的,Flutter不知道你两次传的Painter画的内容是不是一样,所以需要你主动告诉它,多一层优化。
- paint不就是定义绘制规则的吗?:没错,但如果规则完全没变,没必要重复执行paint方法,这个方法就是用来判断规则有没有变的。
- 为啥很多示例直接返回false?:大部分Demo都是静态绘制的场景,没有可变参数,所以直接返回false就行。
- 为啥要强制重写这个方法?:就是怕开发者随便写导致不必要的性能损耗,强制让你根据自己的绘制逻辑做判断。
内容的提问来源于stack exchange,提问作者Nerdy Bunz

