问题
分享当前页面的需求,有时需要对页面进行截图。Flutter
中截图一般使用RepaintBoundary
搭配GlobalKey
获取Widget
的截图(PlatformView
无法截图)。
但是当需要截图的目标是CustomScrollView
时,尤其是使用了SliverPersistentHeader
等Sliver
组件时,不容易想出RepaintBoundary
的嵌套位置,也就无法获取长截图。
方案
百度基本查不到有用的信息,甚至还有一些离奇的方案:
上面长截图的理想情况是 SingleChildScrollView 这种允许我们在 child 套一层 RepaintBoundary,但实际中也可能是 CustomScrollView 这种严格限制 children 类型的,此时我们可能就需要借助类似 SliverToBoxAdapter 这种容器,并且在每个 child 上都套一个 RepaintBoundary
在每个 child
上都套一个 RepaintBoundary
显然是非常非常糟糕的方案,在拍脑袋之前,我们尽可能的多检索一些资料:
https://github.com/SachinGanesh/screenshot/issues/10#issuecomment-586302204
原文:
In your ListView that you wanna take a full screenshot, put a NeverScrollableScrollPhysics on it, then what is going to take control of the scrolling is the SingleChildScrollView.
意思就是给你的CustomScrollView
一个NeverScrollableScrollPhysics
,然后扔到SingleChildScrollView
中进行截图。
很显然这个方案是更合适的,因为不需要关心每一个child
,因为child
的数量可能尤其的多,难以去追踪CustomScrollView
的每一个child
。
解决
组件:
GlobalKey paintKey = GlobalKey();
// 是否处在打印模式
bool printMode = false;
@override
Widget build(BuildContext context) {
// 页面
Widget body = CustomScrollView(
shrinkWrap: printMode, // 注意正常情况下不要滥用shrinkWrap属性。
physics: printMode ? NeverScrollableScrollPhysics() : null,
);
// 截图中
if (printMode) {
body = AbsorbPointer(
child: SingleChildScrollView(
child: RepaintBoundary(
key: paintKey,
child: Container(
color: ColorPlate.lightGray,
child: body,
),
),
),
),
}
return body;
}
截图方法:
_share() async {
setState(() {
printMode = true;
});
await Future.delayed(Duration(milliseconds: 50));
RenderRepaintBoundary boundary =
paintKey.currentContext!.findRenderObject() as RenderRepaintBoundary;
ui.Image image = await boundary.toImage(pixelRatio: 3);
ByteData byteData = (await image.toByteData(
format: ui.ImageByteFormat.png,
))!;
Uint8List pngBytes = byteData.buffer.asUint8List();
print('截图成功 ${pngBytes}')
setState(() {
printMode = false;
});
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。