【開發經驗】Flutter中對CustomScrollView/Sliver元件截長圖

發表於2024-02-23

問題

分享當前頁面的需求,有時需要對頁面進行截圖。Flutter中截圖一般使用RepaintBoundary搭配GlobalKey獲取Widget的截圖(PlatformView 無法截圖)。

但是當需要截圖的目標是CustomScrollView時,尤其是使用了SliverPersistentHeaderSliver元件時,不容易想出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;
    });
  }

相關文章