問題
分享當前頁面的需求,有時需要對頁面進行截圖。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(
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;
});
}