toPng static method

Future<Uint8List> toPng({
  1. required Widget widget,
  2. required Size size,
  3. double pixelRatio = 2.0,
  4. Duration waitBeforeCapture = Duration.zero,
  5. ThemeData? theme,
})

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();
  }
}