Flutter CustomPainter禁止重绘时如何保留已绘制Path供hitTest使用
问题根因
- 你提供的示例代码未显式声明Path成员变量,且
CustomPainter的paint方法仅在shouldRepaint返回true时才会执行,当progress无变化时,新创建的Painter实例的Path始终未被赋值,会导致hitTest时空指针报错。 - 原有逻辑缺少对控件Size变化的判断:就算progress不变,如果控件大小发生改变,原有Path和实际绘制区域不匹配,也需要重新生成Path。
最优解决方案
不需要持续触发重绘,只要新增缓存逻辑复用已生成的Path即可,完整实现代码如下:
class CoolPainter extends CustomPainter { final double progress; // 缓存已生成的Path和对应的Size Path? _cachedPath; Size? _cachedSize; CoolPainter({required this.progress}); static Path _drawRectHole(Size size) { return Path() // 你的绘制逻辑保持不变 ..close(); } @override void paint(Canvas canvas, Size size) { // 仅当缓存失效时重新生成Path if (_cachedSize != size || _cachedPath == null) { _cachedPath = _drawRectHole(size); _cachedSize = size; } canvas.drawPath( _cachedPath!, Paint()..style = PaintingStyle.fill, ); } @override bool hitTest(Offset position) { // 空安全处理,无缓存时默认返回未命中 return _cachedPath?.contains(position) ?? false; } @override bool shouldRepaint(CoolPainter oldDelegate) { // progress发生变化时触发重绘 if (oldDelegate.progress != progress) { return true; } // progress无变化时直接复用旧实例的缓存,不需要重绘 _cachedPath = oldDelegate._cachedPath; _cachedSize = oldDelegate._cachedSize; return false; } }
逻辑说明
- 当progress固定为1.0时,
shouldRepaint返回false,不会触发重绘,同时新实例会直接复用旧实例已缓存的Path,hitTest可以正常执行判断。 - 当控件Size发生变化时,Flutter会强制执行一次paint方法,此时会自动重新生成匹配新尺寸的Path,不需要额外修改判断逻辑。
- Path仅在首次绘制、progress变化、控件Size变化三种场景下重新生成,无任何多余计算和重绘,性能最优。
内容的提问来源于stack exchange,提问作者Alonsormm
相关产品推荐
相关产品推荐

