Flutter-在后台构建,渲染小部件并将其捕获

时间:2019-05-24 16:40:29

标签: flutter flutter-layout

是否可以在舞台外构建和渲染小部件(使其不可见),然后使用RepaintBoundary捕获小部件以将其另存为图像文件?

  

背景:   我想捕获一个已经构建的窗口小部件,但是由于我目前无法提供GlobalKey,因此无法使其与RepaintBoundary一起包装。参见Flutter - RepaintBoundary causes state reset of StatefulWidget

因此,也许有另一种方法是在用户发布捕获时在舞台外重新构建窗口小部件,这次使用RepaintBoundary和GlobalKey,然后将后台渲染的窗口小部件丢弃。

我知道有一个Offstage小部件,但是我不确定如何将其用于此目的。我当前的代码给了我一个失败的断言:

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

这可能是由于Offstage并未按照文档所说的那样绘制:

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

还有另一种方法可以实现这一目标吗?

代码:

要捕获的小部件是child,下面的构建方法是返回child并用GestureDetector包装的方法,该方法实现了捕获,我将其打包在Offstage中捕获的小部件。

  @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,
    );
  }

1 个答案:

答案 0 :(得分:0)

尝试使用Stack小部件,并将Container或任何小部件放在要捕获的小部件上方。

        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);
                },
              ),
            ],
          );