📘

【Flutter】GridView.builderのitemBuilderでreturn nullできなくて混乱した件

2023/01/08に公開

背景

Flutter入門中で、今回GridViewを使って画像の無限スクロールを実装しようとしてました。

今回使用したバージョン

Flutter 3.3.10 • channel stable

TL;DR

  • 無限スクロールでトータル件数分からないとき、GridViewitemCountって何設定するんだ?
  • どうやらitemCount指定しなくても、itemBuilderreturn null;すればスクロール止めれるらしい
  • 実際にreturn null;で試す
    • エラーわず
    • なんで?
  • Flutterの過去verではreturn null;できたらしいが、数年前Flutterのnull-safety対応中にnull許容じゃなくなったらしい(意図的かは私把握できてない)
  • Issueが建てられてて、2022年9月にnull許容復活(まだmasterにマージのみ)
  • でもよくよく考えたら、リスト変わるごとに全体リビルドしてれば、その都度のitemCount指定すれば良かったのでは(今ここ)

最初の思考

  1. Grid表示できるウィジェットあるかな? → GridViewってのがあった
  2. コンストラクタ複数あるけど、画面に表示してる範囲(と前後いくつか)のみレンダリングできるGridView.builderが無限スクロールに適してそう → これ使お。
  3. 引数にitemCount渡すとそこでスクロール終わらせられるらしい
  4. リスト返してる今のAPIじゃトータル件数分かんないどうしよ。。
  5. どうやらitemCount指定しなくても、itemBuilderreturn null;すれば良さそう。実際return null;してる記事も発見。これでいこう

GridView.builderドキュメント

It is legal for itemBuilder to return null. If it does, the scroll view will stop calling itemBuilder, even if it has yet to reach itemCount.

Google翻訳

itemBuilder が null を返すことは正当です。その場合、たとえ itemCount に達していなくても、スクロール ビューは itemBuilder の呼び出しを停止します。

  1. 書いてみた
  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: GridView.builder(
          gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
          ),
          // no itemCount
          itemBuilder: ((context, index) {
            if (index == photos.length) {
              // 次のページ読み込み
              fetchPhotos();
              return const CircularProgressIndicator();
            } else if (index > photos.length) {
              // スクロール終了
              return null;  // <- これ
            }
            return Image.network(photos[index].url);
          }),
        ),
      ),
    );
  }
  1. なんかエラー出てる。なんだ?
The return type 'Null' isn't a 'Widget', as required by the closure's context.

Issueあった

どうやらFlutterの過去verではreturn null;できたが、NNBDというnull-safety関連の対応中にitemBuilderが求める型がnull許容じゃなくなったらしい。(IndexedWidgetBuilder型になってた)

で、↓のIssueで議論して頂いてて、結局2022年9月にnull許容復活した模様(まだmasterにマージのみ)。元々の変更が意図的なものだったかは詳細分からなかった。(読解力ないだけかも)

Issue
https://github.com/flutter/flutter/issues/108705
対応PR
https://github.com/flutter/flutter/pull/108706

でも結局・・・?

リスト変わるごとに全体リビルドしてれば、その都度のitemCount指定すれば良かったのでは(今ここ)。動かしてみた感じ今のとこ問題なさそうだけど、まだFlutterのレンダリングの仕組みやパフォーマンス周り理解できてないのでこの辺の勉強しなきゃいけない。

さいごに

Flutterの公式ドキュメントがどのリリースバージョン指してるのかとか、そもそもFlutterのリリースフロー周りも理解できてないので、この辺も勉強しとかなきゃっていう良い機会になった。
masterにマージされたものは4ヶ月に1回のペースでstableブランチとなってstable版リリースされるってこと?

Discussion