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

自定义RenderProxyBox两次绘制RenderRepaintBoundary类型子组件失效问题

核心结论

该现象不是Flutter的bug,是可滚动组件的专属优化机制导致的,不需要更换RenderObject基类,调整绘制逻辑即可实现需求。

诱因说明
  • 普通静态组件(Container、Column、Row等)没有专属的绘制优化逻辑,每次调用context.paintChild都会按照传入的偏移参数完整执行绘制流程,因此可以正常渲染两次。
  • ListView、SingleChildScrollView这类可滚动组件的内部RenderObject(RenderScrollable、RenderSliver系列)为了提升滚动性能,内置了可视区域裁剪、绘制缓存、绘制状态校验逻辑:第一次调用context.paintChild时,已经更新了子组件内部的绘制状态标记,传入的偏移也已经被写入子组件的parentData。第二次调用paintChild传入新的偏移时,子组件会判定自身没有发生布局、属性变更,不需要触发重绘,直接复用第一次的绘制缓存,不会响应新的偏移参数,因此只会出现一次绘制结果。

上方为正常表现,下方列表为异常表现

修复方案

不需要更换RenderProxyBox基类,只需要调整paint方法逻辑,先将子组件的绘制结果缓存到Picture对象中,再按需绘制多次即可:

@override
void paint(PaintingContext context, Offset offset) {
  if (size.isEmpty || child == null) return;
  // 初始化绘制缓存容器
  final recorder = PictureRecorder();
  final tempContext = PaintingContext(context.containerLayer, Offset.zero & size);
  // 把子组件绘制到缓存中,只绘制一次
  tempContext.paintChild(child!, Offset.zero);
  final picture = tempContext.stopRecordingIfNeeded();
  // 绘制两份缓存结果,偏移自定义
  context.canvas.drawPicture(picture!, offset);
  context.canvas.drawPicture(picture, offset + const Offset(100, 100));
  picture.dispose();
}

注意:如果需要两次绘制的子组件都能正常响应触摸交互,该缓存方案不适用,你需要单独维护两份独立的子组件实例,因为同一个RenderObject节点无法同时绑定两个不同的触摸坐标映射关系。

内容的提问来源于stack exchange,提问作者dasRicardo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 03:42:02