🔥

Riverpodを使って無限スクロールを作る

2023/02/26に公開

概要

画像一覧画面において、画面の一番下までスクロールされた際に自動的に次の画像情報を取得するアプリを作成しまします。
今回は検索したい画像を入力し、「検索」ボタンを押したら画像といいね数・ビューの数の一覧が表示される簡単なアプリを題材に説明したいと思います。

※完成イメージ
flutter_infinite_scroll_with_riverpod

※ソースコード
https://github.com/sukekyo000/flutter_infinite_scroll

前提条件

・Flutterの基本的な書き方が分かる
・riverpodの基本的な書き方が分かる
基本的なUIの説明、riverpodの説明等は省略しているのでご容赦ください。
また、APIはpixabayAPIを使用しておりますが、使用のための手順も省略しているので、
使用方法が知りたい方は以下を参考に使用していただければと思います。
https://pixabay.com/api/docs/

バージョン

開発時のバージョンです
・Flutter 3.3.8
・riverpod 2.1.3

この記事で伝えたいこと

・riverpodを使用して無限スクロールを実現できる

実装方法

1. Providerの作成

今回作成したプロバイダは以下の3つです
・検索した内容の状態を管理するsearchQeuryProvider
・取得する画像のページの状態を管理するpageProvider
・取得した画像の結果の状態を管理するsearchResultProvider

searchQeuryProvider
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/state/search_result_state.dart#L10
pageProvider
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/state/search_result_state.dart#L12
searchResultProvider
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/state/search_result_state.dart#L14-L52

SearchResultクラス
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/state/search_result_state.dart#L54-L71

上記の状態を変化させて、一覧画面を変化させていきます

2. 画面遷移と同時にpageProviderの初期化

「検索」ボタンを押下すると同時に現在のページの初期化と入力した文字の更新を行います。
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/ui/home.dart#L47-L72

ref.watch(searchQeuryProvider.notifier).state = searchQuery;

上記で、検索フォームの値をsearchQeuryProviderで更新し、

ref.read(pageProvider.notifier).state = 1;

上記で、1ページ目から画像を取ってくる処理にします。
※今回使用するAPIのレスポンスがデフォルトで20件の画像情報を取得し、pageを1,2,3...と指定することで、1~20件,21~40件,41~60件...の画像情報を取得できるので、pageを状態管理しています。

3. 検索後、最初に画像情報を取得する処理

Providerでの処理

https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/state/search_result_state.dart#L19-L30
_fetchInitial()で画面遷移後にsearchResultProviderが発火する際の最初の処理を行っています。ここで大切な点は以下2点です

  1. ref.watch(searchQeuryProvider)で検索した内容を取得
  2. _searchResultService.getSearchResultListに検索した内容と最初に取得するページである「1」を渡す

1.ref.watch(searchQeuryProvider)で検索した内容を取得
※別のプロバイダの状態を取得することがAsyncNotifier内で可能になりました
※おすすめ記事
https://zenn.dev/flutteruniv_dev/articles/a8f10da7e80ed0#firestoreに値を追加するasyncnotifier
2._searchResultService.getSearchResultListに検索した内容と最初に取得するページである「1」を渡す
_searchResultService.getSearchResultListは検索クエリとページを指定することでSearchResultクラスを返却する関数です。
本題とはそれるので、解説しませんが、気になる方は以下をご覧ください
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/domain/service/search_result_service.dart

UIでの処理

final searchResult = ref.watch(searchResultProvider);

Consumerウィジェット内で上記コードでsearchResultProviderをwatchします。
AsyncValueなのでdata,error,loadingの3つに分けます。
※全体コード
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/ui/search_result/search_result_page.dart

4. 画面下部に到達したら画像を取得する処理

今回の記事の肝になる部分です。

画面下部のリロード

まず、ListView.builderで各画像のカードを生成しすが、現在取得している画像+1の位置にCircularProgressIndicatorを配置します
https://github.com/sukekyo000/flutter_infinite_scroll/blob/main/lib/ui/search_result/search_result_page.dart#L65-L91
こうすることで取得している画像の下にリロードしているUIを表示できます
※本来はここで、エラーの場合はUIや取得上限に達した場合のUIも構築するべきですが、今回は割愛しています

また、ListView.builderのcontrollerで具体的な処理を実行します。処理の内容は次項で説明します

画面下部のリロード

まず、ConsumerWidget内でScrollControllerを呼びます。

ScrollController scrollController;

このScrollControllerのaddListenerに追記することで、スクロールで検知した特定の条件の処理を追加することができます

そして、画像を取得する条件を
・表示画面の下5%に入ったら
・画像を取得処理していない
の2つの条件にし、画像を取得する処理を行います。

scrollController.addListener(() async {
  if (scrollController.position.pixels >=
	  scrollController.position.maxScrollExtent * 0.95 &&
      !isLoading) {
    isLoading = true;
    ref.read(pageProvider.notifier).state++;
    await ref.read(searchResultProvider.notifier).addImageList();
    isLoading = false;
  }
});

すると、スクロールして画面の下5%に入ると自動的にpageProviderの値が+1され、searchResultProviderのaddImageList()が実行され、新しい画像情報を取得してきます。

まとめ

以上がriverpodでの無限スクロールです。
StatefullWidgetでの無限スクロールの解説記事が多い中、状態管理はriverpodを採用していることが多いので、今回の記事を執筆しました。

間違っている点や質問があれば、是非コメントいただきたいです!

Discussion