LoginSignup
4
3

[Flutter] VGVのvery_good_infinite_listとRiverpodで無限スクロール

Posted at

Very Good Ventures(VGV) が無限スクロール用のパッケージを用意してくれてました。

今回もRiverpodと合わせて使ってみたいと思います。

同じくVGV製のformzを扱った記事を以前書いてます :bow:
[Flutter] formz+riverpodを使ったフォームの実装例

早速ですがサンプルコードです (Github):

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart';

part 'main.g.dart';

@riverpod
class TodoList extends _$TodoList {
  @override
  Future<List<String>> build() async {
    await Future.delayed(const Duration(seconds: 1));
    return List.generate(10, (i) => 'ToDo Item $i');
  }

  Future<void> fetchTodo() async {
    final previousState = await future;

    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      await Future.delayed(const Duration(seconds: 1));
      return previousState +
          List.generate(10, (i) => 'ToDo Item ${previousState.length + i}');
    });
  }
}

void main() => runApp(const ProviderScope(child: MyApp()));

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Simple Example'),
        ),
        body: Consumer(
          builder: (BuildContext context, WidgetRef ref, Widget? child) {
            final AsyncValue<List<String>> val = ref.watch(todoListProvider);

            return InfiniteList(
              itemCount: val.value?.length ?? 0,
              isLoading: val.isLoading,
              onFetchData: ref.read(todoListProvider.notifier).fetchTodo,
              separatorBuilder: (context, index) => const Divider(),
              itemBuilder: (context, index) {
                return ListTile(
                  dense: true,
                  title: Text(val.value?[index] ?? ''),
                );
              },
            );
          },
        ),
      ),
    );
  }
}

ezgif-3-383c3e1813.gif

例によって使いやすい印象です!
formzもそうですが、中の実装を覗いてみるとシンプルな作りになっているようです:eyes:

幾つかInfiniteList のオプションを試してみます。

スクロールの向き

reverse: trueで、スクロールが逆向きになります。チャットっぽいUIにも使えそう

            return InfiniteList(
+             reverse: true,
              itemCount: val.value?.length ?? 0,
              isLoading: val.isLoading,
              onFetchData: ref.read(todoListProvider.notifier).fetchTodo,
              separatorBuilder: (context, index) => const Divider(),
              itemBuilder: (context, index) {

ezgif-3-047651cf61.gif

ローディングのインジケータ

loadingBuilder でローディング用のインジケータの見た目を変更出来ました。
centerLoadingは初期ロードのインジケータの位置の調整でしょうか... :thinking:

            return InfiniteList(
+              loadingBuilder: (context) => +const Center(
+                  child: CircularProgressIndicator(
+                valueColor: AlwaysStoppedAnimation(Colors.purpleAccent),
+              )),
+             centerLoading: true,
              itemCount: val.value?.length ?? 0,
              isLoading: val.isLoading,
              onFetchData: ref.read(todoListProvider.notifier).fetchTodo,
              separatorBuilder: (context, index) => const Divider(),
              itemBuilder: (context, index) {

ezgif-5-eae105e42c.gif

エラー表示

3回目のfetchでエラーが出るようにしておきます

  Future<void> fetchTodo() async {
    final previousState = await future;

    state = const AsyncLoading();
    state = await AsyncValue.guard(() async {
      await Future.delayed(const Duration(seconds: 1));
+      if (previousState.length >= 30) throw 'Unknown Error!!!';
      return previousState +
          List.generate(10, (i) => 'ToDo Item ${previousState.length + i}');
    });

hasErrorerrorBuilder を使うことで、エラー表示をカスタマイズできました:

            return InfiniteList(
+              hasError: val.hasError,
+              errorBuilder: (context) {
+                return Padding(
+                  padding: const EdgeInsets.all(8.0),
+                  child: Container(
+                    padding:
+                        const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0),
+                    decoration: BoxDecoration(
+                      borderRadius: BorderRadius.circular(8.0),
+                      color: Colors.redAccent,
+                    ),
+                    child: Text(
+                      val.error.toString(),
+                      style: const TextStyle(
+                        color: Colors.white,
+                        fontSize: 16.0,
+                      ),
+                    ),
+                  ),
+                );
+              },
              itemCount: val.value?.length ?? 0,

ezgif-5-889f98339f.gif

まとめ

very_good_infinite_listRiverpod で無限スクロールを実装してみました。
hasReachedMaxdebounceDuration などを調整すれば、実際の業務にも使えそうです :dolphin:

もっと良い使い方があれば、ぜひ教えて下さい :bow:

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3