You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter CustomPainter禁止重绘时如何保留已绘制Path供hitTest使用

问题根因
  1. 你提供的示例代码未显式声明Path成员变量,且CustomPainter的paint方法仅在shouldRepaint返回true时才会执行,当progress无变化时,新创建的Painter实例的Path始终未被赋值,会导致hitTest时空指针报错。
  2. 原有逻辑缺少对控件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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.02 15:54:01