0

I'm building a custom RenderObject to draw multiple portions of child widget in different positions and clipped differently (layout, hit testing, semantics and other RenderObject aspects are not revelant here). I managed to get it working, but code breaks for more complex descendants (eg. FlutterLogo() works but Scaffold() not) - child is painted only on the first (or maybe last) paintChild call.

I've read RenderObject documentation and haven't found anything that would prohibit painting the child multiple times during single paint phase. On the other hand, I haven't found any RenderObjects that would do this. I suspect that this won't work when child is creating its own layer and the layer gets reused between paint calls. Wrapping any child in RepaintBoundary seems enough to break it.

class RenderExample extends RenderProxyBox {

  final _clipLayerHandles = [for (int i = 0; i < _gameChildCount; i++) LayerHandle<ClipRectLayer>()];

  //...

  @override
  bool get isRepaintBoundary => true;

  @override
  void dispose() {
    for (final handle in _clipLayerHandles) {
      handle.layer?.dispose();
    }
    super.dispose();
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    for (...) {
      _clipLayerHandles[childIndex].layer = context.pushClipRect(
        needsCompositing,
        offset,
        parentRect,
        (context, offset) => context.paintChild(child!, offset + parentOffset - childOffset),
        oldLayer: _clipLayerHandles[childIndex].layer,
      );
    }
  }
}

Do you have any insights? Is it something that is impossible in the engine? If so, why? Is there any hack to make it work?

mfkw1
  • 920
  • 6
  • 15
  • 1
    cannot you use one `pushClipPath` instead? – pskink Jan 14 '22 at 18:49
  • Unfortunately not, because I want to draw portions of the child in arbitrary positions, eg. top-left quadrant of child in center of parent, and bottom-right quadrant of child in top-right quadrant of parent. I know this seems like an odd use-case but I'm just researching what's possible ;) – mfkw1 Jan 14 '22 at 19:55
  • since your `isRepaintBoundary` is `true` you can always use `OffsetLayer.toImage` and then draw that raster image any times you want – pskink Jan 15 '22 at 06:27
  • Isn't Scene.toImage to slow to call on every repaint? – mfkw1 Jan 15 '22 at 07:07
  • and your child render box changes every single frame? – pskink Jan 15 '22 at 07:11
  • Yes. I'm assuming my child render object can really be anything. – mfkw1 Jan 15 '22 at 07:41
  • i mean if it changes / morphs / animates every single frame - your child render box can still be anything but if it is mostly static you dont have to call `toImage` on every repaint – pskink Jan 15 '22 at 07:50
  • That won't work if child itself animates every single frame. Basically, I just want to know if there's a way to paint child multiple times during single paint phase, assuming I don't want to rasterize it with toImage as you suggested (still, it's a good idea) – mfkw1 Jan 15 '22 at 08:36

0 Answers0