无法在颤动中截取屏幕截图

xba*_*dal 14 flutter

package:flutter/src/rendering/proxy_box.dart': 失败的断言: line 2813 pos 12: '!debugNeedsPaint': 不是真的。

我正在尝试在颤振中截取屏幕截图,但出现异常。我访问了许多链接,但没有任何效果。

Future<Uint8List> _capturePng() async {
    try {
        print('inside');
        RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
        ui.Image image = await boundary.toImage(pixelRatio: 3.0);
        ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
        var pngBytes = byteData.buffer.asUint8List();
        var bs64 = base64Encode(pngBytes);
        print(pngBytes);
        print(bs64);
        setState(() {});
        return pngBytes;
    } catch (e) {
        print(e);
    }
}
Run Code Online (Sandbox Code Playgroud)

exx*_*ain 18

您可以在此处找到官方toImage示例。但看起来它在点击按钮和通话之间没有延迟。toImage

官方仓库有问题:https : //github.com/flutter/flutter/issues/22308

原因是:您的点击初始化按钮的动画RenderObject.markNeedsPaint并被递归调用,包括父项,因此您应该等待一段debugNeedsPaint时间falsetoImage在这种情况下,函数只会抛出断言错误:

  Future<ui.Image> toImage({ double pixelRatio = 1.0 }) {
    assert(!debugNeedsPaint);
    final OffsetLayer offsetLayer = layer;
    return offsetLayer.toImage(Offset.zero & size, pixelRatio: pixelRatio);
  }
Run Code Online (Sandbox Code Playgroud)

https://github.com/flutter/flutter/blob/f0553ba58e6455aa63fafcdca16100b81ff5c3ce/packages/flutter/lib/src/rendering/proxy_box.dart#L2857

  bool get debugNeedsPaint {
    bool result;
    assert(() {
      result = _needsPaint;
      return true;
    }());
    return result;
  }
Run Code Online (Sandbox Code Playgroud)

https://github.com/flutter/flutter/blob/0ca5e71f281cd549f1b5284e339523ad93544c60/packages/flutter/lib/src/rendering/object.dart#L2011

实际上assert函数只在开发中使用,所以你可以看到你不会在生产中遇到错误的麻烦。但我不知道你会遇到什么样的麻烦,可能没有)。

下一个代码不是很好,但它有效:

class _MyHomePageState extends State<MyHomePage> {
  GlobalKey globalKey = GlobalKey();

  Future<Uint8List> _capturePng() async {
    RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();

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

    var image = await boundary.toImage();
    var byteData = await image.toByteData(format: ImageByteFormat.png);
    return byteData.buffer.asUint8List();
  }

  void _printPngBytes() async {
    var pngBytes = await _capturePng();
    var bs64 = base64Encode(pngBytes);
    print(pngBytes);
    print(bs64);
  }

  @override
  Widget build(BuildContext context) {
    return RepaintBoundary(
      key: globalKey,
      child: Center(
        child: FlatButton(
          color: Color.fromARGB(255, 255, 255, 255),
          child: Text('Capture Png', textDirection: TextDirection.ltr),
          onPressed: _printPngBytes
        ),
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Cop*_*oad 5

您的代码已经很好,您在发布模式下应该不会遇到任何问题,因为来自docs

debugNeedsPaint:此渲染对象的绘制信息是否脏。

这仅在调试模式下设置。一般来说,渲染对象不需要根据它们是否脏来调节它们的运行时行为,因为它们应该只在布局和绘制之前立即标记为脏。

它旨在供测试和断言使用。

debugNeedsPaint 为 false 且 debugNeedsLayout 为 true 是可能的(并且确实很常见)。在这种情况下,渲染对象仍将在下一帧中重新绘制,因为在绘制阶段之前,在布局渲染对象之后,框架会隐式调用 markNeedsPaint 方法。

但是,如果您仍然需要解决方案,则可以尝试以下操作:

Future<Uint8List> _capturePng() async {
  try {
    print('inside');
    RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
    
    // if it needs repaint, we paint it.
    if (boundary.debugNeedsPaint) {
      Timer(Duration(seconds: 1), () => _capturePng());
      return null;
    }
    
    ui.Image image = await boundary.toImage(pixelRatio: 3.0);
    ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    var pngBytes = byteData.buffer.asUint8List();
    var bs64 = base64Encode(pngBytes);
    print(pngBytes);
    print(bs64);
    setState(() {});
    return pngBytes;
  } catch (e) {
    print(e);
    return null;
  }
}
Run Code Online (Sandbox Code Playgroud)