見出し画像

さくっとリスト一覧を実装したいときに使える無限スクロール(Infinity Scroll)パターン【Flutter / Riverpod】

この記事では、私が Flutter で無限スクロールを実装する時によく使う方法をご紹介します。無限スクロールを実装する方法は、1つではなく他にも色々ありますが、ここで紹介する方法はその中でもコード量が少ないので(ScrollController を使わないため)ぱぱっとやりたい時におすすめです。
もし ScrollController が他の用途で必要な場合には、素直に ScrollController を使ってしまった方がいいと思います。
今回の実装には Riverpod を使っていますが、もちろん他の方法でも OK です。Riverpod については、こちらをご覧ください。
説明のために実際には API 呼び出しをする部分で、Future で待って文字列の item を追加していく仮の実装をしました。コードは動きます。

最低限実現したいことは以下です
・初期ロード時にローディングアイコンを表示する
・リストの下まで行ったら表示する
・追加ロード時に一番下にローディングアイコンを表示する
・これ以上ロードできない時はローディングアイコンは表示しない

見た目の部分はこちらです:

class InifinityScrollSample extends HookWidget {
 @override
 Widget build(BuildContext context) {
   final List<String> items = useProvider(itemsProvider).state;
   return MaterialApp(
     home: Scaffold(
       body: ListView.builder(
         itemCount: context.read(itemController).listLength,
         itemBuilder: (context, int index) {
           if (index < (items?.length ?? 0)) {
             return Text(items[index]);
           }
           context.read(itemController).loadItemsIfNeeded();
           return const LinearProgressIndicator();
         },
       ),
     ),
   );
 }
}

見た目部分のポイント

・index がリストの長さをこえない時のみ Item にアクセスする
・index がリストの長さをこえた時には新しいアイテムをロードする(同時に、ローディングアイコンを返却する)

ロジックはこちら:

final itemsProvider = StateProvider<List<String>>((ref) => null);
final itemController =
   Provider<ItemController>((ref) => ItemController(ref.read));

class ItemController {
 final Reader _read;
 ItemController(this._read);
 bool _isLoading = false;
 bool _hitTheBottom = false;

 List<String> get _items => _read(itemsProvider).state;
 int get listLength => (_items?.length ?? 0) + (_hitTheBottom ? 0 : 1);

 Future<void> loadItemsIfNeeded() async {
   if (_isLoading || _hitTheBottom) {
     return;
   }
   _isLoading = true;
   await Future.delayed(const Duration(seconds: 1));
   _read(itemsProvider).state = [
     ...(_items ?? []),
     'Item ${_items?.length ?? 0}',
     'Item ${(_items?.length ?? 0) + 1}'
   ];
   _isLoading = false;
   if (10 <= _items.length) {
     _hitTheBottom = true;
   }
 }
}

ロジック部分のポイント

・hitTheBottom によってリストの長さを変更する(まだ一番下に来ていない時はリストを長くする)
・ローディング中の時や一番下に来たときは何もしない
・items の初期値が null なのは、まだロードしていないから空なのか、ロードしたけど空なのかを判定するため

全コードはこちらです:

インフィニティスクロールのやり方は、他にも、色々あります:

@heavenosk さん
https://qiita.com/heavenosk/items/30e9769fcfde5f0fc096

@kikuchyさん
https://qiita.com/kikuchy/items/07d10394a4f7aa2a3836


ご覧いただきありがとうございました。


この記事が気に入ったらサポートをしてみませんか?