自定义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
相关产品推荐
相关产品推荐

