3

Is it possible to build and render a widget off-stage, so not visible, and then capture the widget using a RepaintBoundary to save it as a image file?

Background: I want to capture a widget which is already build, but cant make it work with wrapping it with a RepaintBoundary as i cant supply a GlobalKey at this point. See Flutter - RepaintBoundary causes state reset of StatefulWidget

Therefore maybe a different approach is to just rebuild the widget off-stage at the time a capture is issued by the user, this time with a RepaintBoundary and a GlobalKey, and then throw the offstage rendered widget away.

I know there is a Offstage widget but i am not sure on how to use it for this purpose. My current code gives me a failed assertion:

Failed assertion: line 2752 pos 12: '!debugNeedsPaint': is not true.

This is probably due to that Offstage is not painted as the doc says:

A widget that lays the child out as if it was in the tree, but without painting anything...

Is there another way to achieve this?

Code:

The widget to capture is child, the following build method is the method returning child wrapped with a GestureDetector which implements the capturing, there i packed in the Offstage widget to capture.

  @override
  Widget build(BuildContext context) {

    Widget child = Card(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: children,
        ),
      );

    return GestureDetector(
      onLongPress: () async {

        await showModalBottomSheet(context: context,
            builder: (BuildContext bcontext) {
              return Column(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Offstage(
                    offstage: true,
                    child: RepaintBoundary(
                      key: globalKey,
                      child: child,
                    ),
                  ),
                  ListTile(
                      title: 'Capture Widget,
                      onTap: () async {

                        Map<PermissionGroup, PermissionStatus> permissions = await PermissionHandler().requestPermissions([PermissionGroup.storage]);

                        if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {

                          Directory externalDirectory = await getExternalStorageDirectory();

                          RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();

                          final image = await boundary.toImage(pixelRatio: 2.0);
                          final byteData = await image.toByteData(format: ImageByteFormat.png);

                          String path = externalDirectory.path + '/' + uuid + '.png';

                          File(path).writeAsBytes(byteData.buffer.asUint8List());
                        }

                        Navigator.pop(context);
                      }
                  ),
                  ListTile(
                    title: 'Cancel',
                    onTap: () {
                      Navigator.pop(context);
                    },
                  ),
                ],
              );
            });
      },
      child: child,
    );
  }
user23423294
  • 43
  • 1
  • 5

1 Answers1

3

Try using Stack widget and put a Container or any widget above the widget you want to capture.

        Stack(
       
            children: <Widget>[
               RepaintBoundary(
                  key: globalKey,
                  child: child,
                ),
              Container(
                 color: Colors.white,
                 height: MediaQuery.of(context).size.height,
                 width: MediaQuery.of(context).size.width,
              ),
              ListTile(
                  title: 'Capture Widget',
                  onTap: () async {

                    Map<PermissionGroup, PermissionStatus> permissions = await PermissionHandler().requestPermissions([PermissionGroup.storage]);

                    if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {

                      Directory externalDirectory = await getExternalStorageDirectory();

                      RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();

                      final image = await boundary.toImage(pixelRatio: 2.0);
                      final byteData = await image.toByteData(format: ImageByteFormat.png);

                      String path = externalDirectory.path + '/' + uuid + '.png';

                      File(path).writeAsBytes(byteData.buffer.asUint8List());
                    }

                    Navigator.pop(context);
                  }
              ),
              ListTile(
                title: 'Cancel',
                onTap: () {
                  Navigator.pop(context);
                },
              ),
            ],
          ); 
wujek
  • 10,112
  • 12
  • 52
  • 88
diegoveloper
  • 93,875
  • 20
  • 236
  • 194