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