screenshot

repository·master·Indexed 18 days ago

https://github.com/sachinganesh/screenshot

A Flutter package to capture widgets as images. It supports capturing visible widgets via the Screenshot widget and ScreenshotController, as well as invisible or long widgets using captureFromWidget and captureFromLongWidget. Features include saving images to files via captureAndSave, adjusting image quality with pixelRatio, and handling rendering delays. Note: Platform Views (e.g., Google Maps) are not supported, and captureAndSave is not available on Web.

Tokens
4.3K
Snippets
13
Records
17
Agent score
63%

What's inside screenshot

  1. Improve image quality with pixelRatio

    master

    If captured images appear pixelated, adjust the pixelRatio parameter in the capture() method. The pixelRatio defines the scale between logical pixels and the output image size.

    To match the device's native resolution, use MediaQuery.of(context).devicePixelRatio.

    double pixelRatio = MediaQuery.of(context).devicePixelRatio;
    
    screenshotController.capture(
        pixelRatio: pixelRatio
    )
  2. Get Started with Screenshot

    master

    To capture visible widgets, follow these three steps:

    1. Create a ScreenshotController instance: This controller manages the capture process.
    2. Wrap your target widget: Use the Screenshot widget and pass your controller to the controller property.
    3. Call capture(): Use the controller to trigger the capture, which returns a Uint8List representing the image.

    Note: The package works by wrapping widgets inside a RenderRepaintBoundary.

    // 1. Create instance
    ScreenshotController screenshotController = ScreenshotController(); 
    
    // 2. Wrap widget
    Screenshot(
        controller: screenshotController,
        child: Text("This text will be captured as image"),
    ),
    
    // 3. Capture
    screenshotController.capture().then((Uint8List image) {
        // Handle image
    });
  3. Customize iOS Launch Screen Assets

    master

    To change the appearance of the launch screen in your iOS application, you can replace the existing image files within the LaunchImage.imageset directory with your own assets.

    Alternatively, you can manage these assets using Xcode:

    1. Open your Flutter project's iOS workspace using open ios/Runner.xcworkspace.
    2. In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
    3. Drag and drop your desired images into the asset catalog to replace the current launch images.
    open ios/Runner.xcworkspace
  4. Capture widgets not in the widget tree using ScreenshotController

    master

    If you need to capture a widget that is not currently being rendered in the application's widget tree, use the static methods provided by ScreenshotController. This is useful for generating images of specific components in isolation.

    Methods:

    • ScreenshotController.widgetToUiImage(widget, {delay, pixelRatio, context, targetSize}): Renders the widget in an off-screen view and returns a ui.Image.
    • ScreenshotController.captureFromWidget(widget, {delay, pixelRatio, context, targetSize}): Returns the captured widget as a Uint8List (PNG).
    • ScreenshotController.captureFromLongWidget(widget, {delay, pixelRatio, context, constraints}): Specifically designed for large widgets. It calculates the required size of the widget before capturing.

    Usage Constraints for captureFromLongWidget:

    1. Do not use scrolling widgets like ListView or GridView. Instead, use Column or Row.
    2. Do not use layout widgets like Flexible, Expanded, or Spacer without passing explicit constraints, as they require a parent with defined constraints to function.
    // Capturing a widget that isn't in the tree
    Uint8List bytes = await ScreenshotController.captureFromWidget(
      MyStandaloneWidget(),
      delay: Duration(seconds: 1),
      context: context, // Recommended to inherit Theme and MediaQuery
    );
    
    // Capturing a long widget (e.g., a large form or list-like structure)
    Uint8List longBytes = await ScreenshotController.captureFromLongWidget(
      MyLongWidget(),
      constraints: BoxConstraints(maxWidth: 500),
      delay: Duration(seconds: 1),
    );
  5. Use ScreenshotController to capture widgets in the tree

    master

    The ScreenshotController can be used to capture images of widgets that are currently part of the active widget tree. To do this, you must wrap the target widget in a Screenshot widget, which provides the necessary RepaintBoundary and links it to the controller via a GlobalKey.

    Key Methods:

    • capture(): Returns the captured widget as a Uint8List (PNG format).
    • captureAsUiImage(): Returns the captured widget as a ui.Image.
    • captureAndSave(directory, {fileName, pixelRatio, delay}): Captures the widget and saves it directly to the specified directory as a file.

    Important Note: A delay is required to ensure the widget has finished rendering. For larger widget trees, increase the delay (e.g., 1 second).

    final ScreenshotController controller = ScreenshotController();
    
    // In your build method:
    Screenshot(
      controller: controller,
      child: MyWidgetToCapture(),
    );
    
    // To capture:
    Uint8List? bytes = await controller.capture();
    
    // To capture and save to a path:
    String? filePath = await controller.captureAndSave(
      '/path/to/directory', 
      fileName: 'my_image.png'
    );
  6. Fixing pixelation or incorrect frames with delay

    master

    Sometimes screenshots may capture the previous frame or fail to capture raster graphics (like images) because the GPU hasn't finished rasterizing the current frame.

    Solution: Add a small delay to the capture() method to allow the frame to settle.

    screenshotController.capture(delay: Duration(milliseconds: 10))
  7. Share captured images

    master

    To share a screenshot, first capture the image as a Uint8List, save it to a temporary file using dart:io, and then use a sharing plugin (like share_plus) to share the file path.

    await _screenshotController.capture(delay: const Duration(milliseconds: 10)).then((Uint8List image) async {
          if (image != null) {
            final directory = await getApplicationDocumentsDirectory();
            final imagePath = await File('${directory.path}/image.png').create();
            await imagePath.writeAsBytes(image);
    
            /// Share Plugin
            await Share.shareFiles([imagePath.path]);
          }
        });
  8. Capture long widgets with captureFromLongWidget

    master

    To capture long widgets (like a long list) that are not currently visible, use captureFromLongWidget.

    Important Implementation Rules:

    • Use a Column to hold children instead of a scrolling widget.
    • Do not use Expanded, Flexible, or Spacer inside the long widget.
    • You can provide constraints (e.g., BoxConstraints) to define the maximum size of the output image.
    • It is recommended to wrap the widget in InheritedTheme.captureAll and a Material widget to ensure correct rendering.
    screenshotController
          .captureFromLongWidget(
              InheritedTheme.captureAll(
                context, 
                Material(
                  child: myLongWidget,
                ),
              ),
              delay: Duration(milliseconds: 100),
              context: context,
              // constraints: BoxConstraints(maxHeight: 1000, maxWidth: 1000),
          )
          .then((capturedImage) {
        // Handle captured image
      });
  9. Capture invisible widgets with captureFromWidget

    master

    You can capture widgets that are not currently part of the active widget tree by using the captureFromWidget method. This is useful for generating images of UI components that are not currently rendered on the screen.

    screenshotController
          .captureFromWidget(Container(
              padding: const EdgeInsets.all(30.0),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blueAccent, width: 5.0),
                color: Colors.redAccent,
              ),
              child: Text("This is an invisible widget")))
          .then((capturedImage) {
        // Handle captured image
      });