Reputation: 497
I'm looking for a solution to draw a Flutter Widget tree in a separate graphic pipeline that will never be displayed on the screen. I want to be able to define the width/height constraints to any size regardless of the display.
The goal is to get a png image of this Widget that is not dependent on the device, to send it to a printer. I know it's possible to use RepaintBundary
to build an image but that's only for something displayed on the screen.
I tested something like this, with no luck:
Future<void> capture(BuildContext context, Widget widget) async {
final rect = Rect.fromLTWH(0, 0, 200, 200);
final root = OffscreenPainter(
size: Size(200, 200),
child: ConstrainedBox(
constraints: BoxConstraints.tightFor(width: 200, height: 200),
child: widget,
));
final ro = root.createRenderObject(context);
final el = root.createElement();
// el.activate();
el.attachRenderObject(ro);
final o = PipelineOwner();
// ro.attach(o);
o.rootNode = ro;
ro.scheduleInitialLayout();
final rootLayer = OffsetLayer();
rootLayer.attach(o);
ro.scheduleInitialPaint(rootLayer);
el.updateChildren();
ro.layout(BoxConstraints(maxWidth: rect.width, maxHeight: rect.height));
o.flushLayout();
o.flushCompositingBits();
o.flushPaint();
final im = await ro.toImage();
final bd = await im.toByteData(format: ImageByteFormat.png);
final f = File('image.png');
f.writeAsBytesSync(bd.buffer.asUint8List());
print('saved to ${f.absolute}');
}
class OffscreenPainter extends SingleChildRenderObjectWidget {
/// Creates a widget that isolates repaints.
const OffscreenPainter({Key key, Widget child, @required this.size})
: super(key: key, child: child);
final Size size;
@override
RenderOffscreenPainter createRenderObject(BuildContext context) =>
RenderOffscreenPainter(size);
}
class RenderOffscreenPainter extends RenderRepaintBoundary {
RenderOffscreenPainter(this._size);
final Size _size;
@override
void performLayout() {
if (child != null) {
child.layout(BoxConstraints.tight(_size));
size = child.size;
} else {
size = _size;
}
}
}
Upvotes: 4
Views: 1793
Reputation: 17804
You can use the Transform Widget to push your widget off-screen. According to the docs, Transform.translate moves the widget before it's painted.
In this example, I added an Icon to the bottom navigation bar and shifted it down. It doesn't show but I can still access the RepaintBoundary.
Transform.translate(
offset: Offset(0,200),
child: RepaintBoundary(
key: iconKey,
child: IconButton(icon: Icon(Icons.star),
onPressed: () {
// Do something
}),
),
),
Upvotes: 2