renderFlutterWidget static method

Future<String?> renderFlutterWidget(
  1. Widget widget, {
  2. required String key,
  3. Size logicalSize = const Size(300, 300),
  4. double? pixelRatio,
})

Rasterises an arbitrary Flutter widget to a PNG in shared storage and returns its path, for display through MFileImage.

The escape hatch for anything the DSL cannot express — a chart, a CustomPaint, a layout with no widget-safe equivalent. The cost is that the result is a static bitmap: it does not adapt to light/dark or to the widget's real size, and it has to be re-rendered whenever the content changes. Prefer real DSL nodes where they exist, and reach for this when they do not.

logicalSize is in logical pixels and should match the space the image occupies. Keep it modest on Android: a RemoteViews update carries the bitmap across a Binder transaction, and an oversized one silently drops the entire update.

Implementation

static Future<String?> renderFlutterWidget(
  Widget widget, {
  required String key,
  Size logicalSize = const Size(300, 300),
  double? pixelRatio,
}) async {
  final repaint = RenderRepaintBoundary();
  final view = WidgetsBinding.instance.platformDispatcher.views.first;
  final ratio = pixelRatio ?? view.devicePixelRatio;

  final renderView = RenderView(
    view: view,
    child: RenderPositionedBox(child: repaint),
    configuration: ViewConfiguration(
      physicalConstraints: BoxConstraints.tight(logicalSize) * ratio,
      logicalConstraints: BoxConstraints.tight(logicalSize),
      devicePixelRatio: ratio,
    ),
  );

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

  final buildOwner = BuildOwner(focusManager: FocusManager());
  final element = RenderObjectToWidgetAdapter<RenderBox>(
    container: repaint,
    child: Directionality(
      textDirection: TextDirection.ltr,
      child: MediaQuery(
        data: MediaQueryData.fromView(view),
        child: widget,
      ),
    ),
  ).attachToRenderTree(buildOwner);

  buildOwner
    ..buildScope(element)
    ..finalizeTree();
  pipeline
    ..flushLayout()
    ..flushCompositingBits()
    ..flushPaint();

  final image = await repaint.toImage(pixelRatio: ratio);
  final data = await image.toByteData(format: ui.ImageByteFormat.png);
  if (data == null) return null;
  return saveFile(key, data.buffer.asUint8List());
}