Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Take screenshot hiding some widgets in Flutter

Tags:

flutter

dart

I want to take a screenshot of a screen but I want to hide/blur/paint some Widgets in the resulting screenshot, but not in the app. Something like this:

What we see in the app What I want to get in the screenshot

The first picture is what I want to see in the app, no changes. The second picture is what I want to see in the screenshot, a specific widget painted.

My first thought was to calculate absolute coordinates of the specific widget, but in order to do this, I should add a GlobalKey to each of the widgets of the screen, and this is not feasible for my case.

How to do this without adding a GlobalKey to each of the widgets of the screen?

My current approach for taking a screenshot is:

final pixelRatio = MediaQuery.of(context).devicePixelRatio;
final boundary = boundaryKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
final image = await boundary.toImage(pixelRatio: pixelRatio);

This is what I'm using to get coordinates of a Widget that has a GlobalKey:

extension GlobalKeyExtension on GlobalKey {
  Rect? get globalPaintBounds {
    final renderObject = currentContext?.findRenderObject();
    var translation = renderObject?.getTransformTo(null).getTranslation();
    if (translation != null) {
      return renderObject!.paintBounds
          .shift(Offset(translation.x, translation.y));
    } else {
      return null;
    }
  }
}
like image 414
CarlosMacMar Avatar asked Jan 22 '26 17:01

CarlosMacMar


1 Answers

I think you need to declare:

bool _hideWidget = false;

Next you this value with all widgets that you want to hide while taking screenshot:

floatingActionButton: _hideWidget ? const SizedBox.shrink() : FloatingActionButton(
            onPressed: _incrementCounter,
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),

In method that you use for taking screenshot, use next:

Future takeScreenShot() async {
    final RenderRepaintBoundary boundary = previewContainer.currentContext!.findRenderObject()! as RenderRepaintBoundary;

    if (boundary.debugNeedsPaint) {
      print("Waiting for boundary to be painted.");
      await Future.delayed(const Duration(milliseconds: 20));
      return takeScreenShot();
    }

    setState(() {
      _hideWidget = !_hideWidget;
    });
    await Future.delayed(const Duration(seconds: 1));
    final ui.Image image = await boundary.toImage(pixelRatio: 5);
    final directory = (await getApplicationDocumentsDirectory()).path;
    final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    final Uint8List pngBytes = byteData!.buffer.asUint8List();
    File imgFile = File('$directory/screenshot.png');
    print('$directory/screenshot.png');
    imgFile.writeAsBytes(pngBytes);
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      _hideWidget = !_hideWidget;
    });
  }

PS: in asynchronius methods you must use FutureDelayed for properly working of SetState

like image 63
Murat Kurbanov Avatar answered Jan 25 '26 10:01

Murat Kurbanov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!