1

I have a non-interactive (i.e. no GestureDetector, etc.) widget subtree that I would like to be drawn identically > 1 time.

Currently I am achieving this by placing the widget subtree into the tree multiple times. There are some downsides to this:

  1. Each StatefulWidget in the subtree is given its own unique State instance, whereas a single one would suffice and be preferable.

  2. Performance: identical redundant layout and painting is performed for each subtree.

If possible, I would like to manually render (layout and paint) the subtree just once, and then draw the resulting rendering as needed.

I am able to draw to a separate canvas with PictureRecorder and paint the resulting picture as needed using Canvas.drawPicture().

What I am missing is how to perform layout and painting of widget trees in Flutter. I've not been successful in finding any information about this, perhaps because I'm unaware of the best search term to use.

What I'm looking for is high-level pointers about how to approach manual rendering of widget subtrees, including any links to relevant documentation, articles, example code that does something similar, or even just search terms that will lead me in the right direction. Legitimate, reasoned criticisms of this approach are also welcome from those with first-hand experience attempting something similar. :)

Thank you!

Chuck Batson
  • 2,165
  • 1
  • 17
  • 15
  • 1
    i never tried it but what about `Offstage`? – pskink Jan 25 '23 at 05:11
  • @pskink Thank you for the useful suggestion. `Offstage` itself may not do the trick, but thanks to your hint it occurs to me I may be able do something very similar: a custom `SingleChildRenderObjectWidget` which overrides `paint()` to paint its child onto a separate canvas – Chuck Batson Jan 25 '23 at 06:05

1 Answers1

0

Although not directly answering the question of "how to manually layout and paint a widget subtree," an approach which achieves a similar net effect (disregarding performance differences) toward the stated goal of "a non-interactive widget subtree to be drawn identically > 1 time" is to paint the subtree multiple times:

/// Caveat: if the transform is accessed (either directly, or
/// indirectly via [RenderBox.globalToLocal], etc.) outside of
/// painting, the transform will be the one that would apply for
/// an offset of [Offset.zero].
class MultiOffsetPainter extends SingleChildRenderObjectWidget {
  final List<Offset> offsets;

  const MultiOffsetPainter({super.key, super.child, required this.offsets});

  @override
  RenderObject createRenderObject(BuildContext context) {
    return _MultiOffsetPainterRenderObject(offsets);
  }

  @override
  void updateRenderObject(BuildContext context, _MultiOffsetPainterRenderObject renderObject) { // ignore: library_private_types_in_public_api
    renderObject.offsets = offsets;
  }
}

class _MultiOffsetPainterRenderObject extends RenderProxyBox {
  List<Offset> offsets;
  Offset? _currentTranslation;

  _MultiOffsetPainterRenderObject(this.offsets);

  @override
  void applyPaintTransform(RenderObject child, Matrix4 transform) {
    if (_currentTranslation != null) {
      transform.translate(_currentTranslation!.dx, _currentTranslation!.dy, 0);
    }
    super.applyPaintTransform(child, transform);
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    for (var translation in offsets) {
      _currentTranslation = translation;
      super.paint(context, offset + translation);
      _currentTranslation = null;
    }
  }
}

Note the important caveat.

Cataloging here on the chance this might be useful to a future reader.

Chuck Batson
  • 2,165
  • 1
  • 17
  • 15