Last active
October 12, 2025 07:27
-
-
Save AlexV525/612da609aae83cc572d6c051667bf66d to your computer and use it in GitHub Desktop.
How do we create images through `RepaintBoundary` or `Widget` in Flutter?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Author: Alex Li (https://github.com/AlexV525) | |
| // Date: 2025/10/10 | |
| import 'dart:typed_data'; | |
| import 'dart:ui' as ui; | |
| import 'package:flutter/rendering.dart'; | |
| import 'package:flutter/widgets.dart'; | |
| /// Create an image from given [GlobalKey], which is attached to an exist | |
| /// [RepaintBoundary]. | |
| /// | |
| /// [imageSize] can define what size the generated image will be (in pixels). | |
| Future<Uint8List?> createImageFromRepaintBoundary( | |
| GlobalKey boundaryKey, { | |
| double? pixelRatio, | |
| Size? imageSize, | |
| ui.ImageByteFormat format = ui.ImageByteFormat.png, | |
| }) async { | |
| assert( | |
| boundaryKey.currentContext?.findRenderObject() is RenderRepaintBoundary, | |
| ); | |
| final boundary = boundaryKey.currentContext?.findRenderObject() as RenderRepaintBoundary; | |
| final constraints = boundary.constraints; | |
| double? outputRatio = pixelRatio; | |
| if (imageSize != null) { | |
| outputRatio = imageSize.width / constraints.maxWidth; | |
| } | |
| final view = ui.PlatformDispatcher.instance.implicitView!; | |
| final ui.Image image = await boundary.toImage( | |
| pixelRatio: outputRatio ?? MediaQueryData.fromView(view).devicePixelRatio, | |
| ); | |
| final byteData = await image.toByteData( | |
| format: format, | |
| ); | |
| final imageData = byteData?.buffer.asUint8List(); | |
| return imageData; | |
| } | |
| /// Creates an image from the given widget by first spinning up a element and | |
| /// render tree, then waiting for the given [wait] amount of time and then | |
| /// creating an image via a [RepaintBoundary]. | |
| /// | |
| /// The final image will be of size [imageSize] and the the widget will be | |
| /// layout, with the given [logicalSize]. | |
| Future<Uint8List?> createImageFromWidget( | |
| Widget widget, { | |
| Duration? wait, | |
| Size? logicalSize, | |
| Size? imageSize, | |
| }) async { | |
| final view = ui.PlatformDispatcher.instance.implicitView!; | |
| final repaintBoundary = RenderRepaintBoundary(); | |
| logicalSize ??= view.physicalSize / view.devicePixelRatio; | |
| imageSize ??= view.physicalSize; | |
| final renderView = RenderView( | |
| view: view, | |
| child: RenderPositionedBox( | |
| alignment: Alignment.center, | |
| child: repaintBoundary, | |
| ), | |
| configuration: ViewConfiguration( | |
| physicalConstraints: BoxConstraints.tight(view.physicalSize), | |
| logicalConstraints: BoxConstraints.tight(logicalSize), | |
| devicePixelRatio: view.devicePixelRatio, | |
| ), | |
| ); | |
| final pipelineOwner = PipelineOwner(); | |
| final buildOwner = BuildOwner(focusManager: FocusManager()); | |
| pipelineOwner.rootNode = renderView; | |
| renderView.prepareInitialFrame(); | |
| final rootElement = RenderObjectToWidgetAdapter<RenderBox>( | |
| container: repaintBoundary, | |
| child: Directionality( | |
| textDirection: TextDirection.ltr, | |
| child: widget, | |
| ), | |
| ).attachToRenderTree(buildOwner); | |
| buildOwner.buildScope(rootElement); | |
| if (wait != null) { | |
| await Future<void>.delayed(wait); | |
| } | |
| buildOwner.buildScope(rootElement); | |
| buildOwner.finalizeTree(); | |
| pipelineOwner.flushLayout(); | |
| pipelineOwner.flushCompositingBits(); | |
| pipelineOwner.flushPaint(); | |
| final image = await repaintBoundary.toImage( | |
| pixelRatio: imageSize.width / logicalSize.width, | |
| ); | |
| final byteData = await image.toByteData( | |
| format: ui.ImageByteFormat.png, | |
| ); | |
| final imageData = byteData?.buffer.asUint8List(); | |
| return imageData; | |
| } |
Author
For Flutter 3.13, it's not working on IOS 17.
Sounds weird because this is not based on underlying platforms. It could be a problem of Impeller though.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For Flutter 3.13, it's not working on IOS 17.