fromWidget static method

Future<WidgetWrapper> fromWidget({
  1. required BuildContext context,
  2. required Widget widget,
  3. required BoxConstraints constraints,
  4. double pixelRatio = 1.0,
  5. PdfImageOrientation? orientation,
  6. double? dpi,
})

Wrap a Flutter Widget to an ImageProvider.

final wrapped = await WidgetWrapper.fromWidget(
  widget: Container(
    color: Colors.white,
    child: Text(
      'Hello world !',
      style: TextStyle(color: Colors.amber),
    ),
  ),
  constraints: BoxConstraints(maxWidth: 100, maxHeight: 400),
  pixelRatio: 3,
);

pdf.addPage(
  pw.Page(
    pageFormat: format,
    build: (context) {
      return pw.Image(wrapped, width: 100);
    },
  ),
);

Implementation

static Future<WidgetWrapper> fromWidget({
  required BuildContext context,
  required Widget widget,
  required BoxConstraints constraints,
  double pixelRatio = 1.0,
  PdfImageOrientation? orientation,
  double? dpi,
}) async {
  assert(pixelRatio > 0);

  if (!constraints.hasBoundedHeight || !constraints.hasBoundedHeight) {
    throw Exception(
        'Unable to convert an unbounded widget. Add maxWidth and maxHeight to the constraints.');
  }

  widget = ConstrainedBox(
    constraints: constraints,
    child: widget,
  );

  final prop = DiagnosticPropertiesBuilder();
  widget.debugFillProperties(prop);

  if (prop.properties.isEmpty) {
    throw ErrorDescription('Unable to get the widget properties');
  }

  final computedConstraints = prop.properties
      .whereType<DiagnosticsProperty<BoxConstraints>>()
      .first
      .value;

  if (computedConstraints == null ||
      !computedConstraints.hasBoundedWidth ||
      !computedConstraints.hasBoundedWidth) {
    throw Exception('Unable to convert an unbounded widget.');
  }

  final repaintBoundary = RenderRepaintBoundary();
  final view = View.of(context);

  final renderView = RenderView(
    child: RenderPositionedBox(
        alignment: Alignment.center, child: repaintBoundary),
    configuration: ViewConfiguration.fromView(view),
    view: view,
  );

  final pipelineOwner = PipelineOwner()..rootNode = renderView;
  renderView.prepareInitialFrame();

  final buildOwner = BuildOwner(focusManager: FocusManager());
  final rootElement = RenderObjectToWidgetAdapter<RenderBox>(
    container: repaintBoundary,
    child: Directionality(
      textDirection: TextDirection.ltr,
      child: IntrinsicHeight(child: IntrinsicWidth(child: widget)),
    ),
  ).attachToRenderTree(buildOwner);

  buildOwner
    ..buildScope(rootElement)
    ..finalizeTree();

  pipelineOwner
    ..flushLayout()
    ..flushCompositingBits()
    ..flushPaint();

  final image = await repaintBoundary.toImage(pixelRatio: pixelRatio);
  final bytes = await image.toByteData(format: ui.ImageByteFormat.rawRgba);
  if (bytes == null) {
    throw Exception('Unable to read image data');
  }

  return WidgetWrapper._(
    bytes.buffer.asUint8List(),
    image.width,
    image.height,
    orientation ?? PdfImageOrientation.topLeft,
    dpi,
  );
}