Closed7

SingleChildScrollViewではなくSliverListを使ったらパフォーマンスが劇的に変わった件

enoiuenoiu

今までSingleChildScrollViewを使ってPDFを表示していたが、大きなファイルだとメモリ不足でアプリが落ちる問題が報告されていた。

SingleChildScrollViewでは、childの中身がすべて描画されるので、ListView.builderを使うと良いらしい。

https://zenn.dev/hasumi/articles/980e503149a343

enoiuenoiu

ただし、複数のPDFファイルを連続して表示する機能がある都合上、それぞれのPDFファイルごとにListViewを作る必要がある。
つまり、ListViewの中に複数のListViewがあるという形なので、中のListViewのサイズが指定されないとエラーが出る。
これを修正するためにshrinkWrap: trueにすることができるが、そうすると、結局中のウィジェットがすべて構築されてしまうとのこと。
https://tech.excite.co.jp/entry/2024/10/01/115451

enoiuenoiu

なお、shrinkWrapを使わずに、ListViewのサイズを指定してみたが、こうした場合も中のウィジェットがすべて構築されてしまうっぽい(パフォーマンスに変化がなかった)。

SingleChildScrollViewListView.builder, Sliverのパフォーマンスの比較については、以下の記事がわかりやすい。
https://zenn.dev/koichi_51/articles/a2c028a65dc7fe

ということで、Sliverを使うことに。
(実は以前使おうとしたのだが、その際うまくできなかったので一旦諦めていた)

enoiuenoiu

全体のListView.builderCustomScrollViewにして、itemCountitemBuildersliversとなる。

CustomScrollView(
  //
  slivers: List.generate(
    _list.length,
    (index) {
      return _pdfView(index),
    }
  ),
)
enoiuenoiu

sliversの中は、その直下がsliver関係のWidgetとなる必要がある。

PDF読込中にCircularProgressIndicatorを表示するようにしているが、これについてはSliverToBoxAdapterで囲う。

SliverToBoxAdapter(
  child: const Center(child: CircularProgressIndicator()),
)
enoiuenoiu

実際にそれぞれのPDFファイルを表示する部分は、SliverListを使う。

SliverList(
  delegate: SliverChildBuilderDelegate(
    (context, pageIndex) {
    //
    },
    childCount: length,
  ),
)
enoiuenoiu

これによりパフォーマンスがかなり改善した。

実際にDev ToolsのPerformance viewを見てみると、前は8fpsとかしか出てなかったのが、40fpsを余裕で超えるようになった。

ページをジャンプした際にはページが完全に表示されるまで若干の時間がかかるが、1秒もないくらいだったので、十分許容範囲内だと感じた。

このスクラップは2024/11/25にクローズされました