package:flutter / src / rendering / proxy_box.dart':失败的断言: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);
}
}
答案 0 :(得分:1)
您的代码已经很好,在发布模式下您应该不会遇到任何问题,因为来自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;
}
}
答案 1 :(得分:1)
您可以找到官方toImage
示例here。但是看来在按钮点击和toImage
调用之间没有延迟就无法正常工作。
官方存储库中存在一个问题:https://github.com/flutter/flutter/issues/22308
原因在于:您的点击会初始化按钮的动画,并且RenderObject.markNeedsPaint
将被递归调用(包括父母),因此您应该等待debugNeedsPaint
再次成为false
时。 toImage
函数在这种情况下只会引发断言错误:
Future<ui.Image> toImage({ double pixelRatio = 1.0 }) {
assert(!debugNeedsPaint);
final OffsetLayer offsetLayer = layer;
return offsetLayer.toImage(Offset.zero & size, pixelRatio: pixelRatio);
}
bool get debugNeedsPaint {
bool result;
assert(() {
result = _needsPaint;
return true;
}());
return result;
}
实际上,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
),
),
);
}
}
答案 2 :(得分:1)
我将返回的逻辑从null再次更改为_capturePng()函数。
Reff:@CopsOnRoad
检查以下代码
Future<Uint8List> _capturePng() async {
try {
print('inside');
RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
// if it needs repaint, we paint it.
if (boundary.debugNeedsPaint) {
//if debugNeedsPaint return to function again.. and loop again until boundary have paint.
return _capturePng());
}
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;
}
}
答案 3 :(得分:0)
我遇到了同样的问题
经过长时间的研究
我终于找到问题了
如果要捕获的小部件位于ListView中,并且列表视图很长,则您的小部件不在屏幕上,捕获将失败。
答案 4 :(得分:0)
这是对已接受答案的重要补充。根据 debugNeedsPaint
的文档,在发布模式下检查此标志会使应用程序崩溃 (In release builds, this throws.)
所以我们只需要在调试模式下检查这个标志,使用 kDebugMode
中的 import 'package:flutter/foundation.dart';
var debugNeedsPaint = false;
//https://stackoverflow.com/questions/49707028/how-to-check-flutter-application-is-running-in-debug
//In release builds, this throws (boundary.debugNeedsPaint)
if (kDebugMode) debugNeedsPaint = boundary.debugNeedsPaint;
if (debugNeedsPaint) {
print("Waiting for boundary to be painted.");
await Future.delayed(const Duration(milliseconds: 20));
return _capturePng();
}