toPng static method
waitBeforeCapture gives async work in the widget (network images,
etc.) a chance to land before capture.
Implementation
static Future<Uint8List> toPng({
required Widget widget,
required Size size,
double pixelRatio = 2.0,
Duration waitBeforeCapture = Duration.zero,
ThemeData? theme,
}) async {
WidgetsFlutterBinding.ensureInitialized();
final repaintBoundary = RenderRepaintBoundary();
final flutterView = WidgetsBinding.instance.platformDispatcher.views.first;
final renderView = RenderView(
view: flutterView,
configuration: ViewConfiguration(
physicalConstraints: BoxConstraints.tight(size * pixelRatio),
logicalConstraints: BoxConstraints.tight(size),
devicePixelRatio: pixelRatio,
),
child: RenderPositionedBox(
alignment: Alignment.center,
child: repaintBoundary,
),
);
final pipelineOwner = PipelineOwner();
final buildOwner = BuildOwner(focusManager: FocusManager());
pipelineOwner.rootNode = renderView;
renderView.prepareInitialFrame();
Widget tree = MediaQuery(
data: MediaQueryData(size: size, devicePixelRatio: pixelRatio),
child: Directionality(
textDirection: TextDirection.ltr,
child: SizedBox.fromSize(size: size, child: widget),
),
);
if (theme != null) {
tree = Theme(data: theme, child: tree);
}
// ignore: deprecated_member_use
final rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: tree,
).attachToRenderTree(buildOwner);
buildOwner
..buildScope(rootElement)
..finalizeTree();
pipelineOwner
..flushLayout()
..flushCompositingBits()
..flushPaint();
if (waitBeforeCapture > Duration.zero) {
await Future<void>.delayed(waitBeforeCapture);
pipelineOwner
..flushLayout()
..flushCompositingBits()
..flushPaint();
}
final ui.Image image =
await repaintBoundary.toImage(pixelRatio: pixelRatio);
try {
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) {
throw StateError('toByteData returned null for rendered widget');
}
return byteData.buffer.asUint8List();
} finally {
image.dispose();
}
}