🐵

Flutterで無限スクロールしたい

2025/02/18に公開

課題

一覧ページでDBからユーザに紐づく全データを一括で取得しており、表示速度が低下していました。

やりたいこと

Xを例に出すと、特定のユーザの過去の投稿を遡るときに、数十件ずつをこまめに表示するアレです。
下までスクロールするとインジケータがぐるぐる回って、次の数十件が読み込まれます。
分割してAPIからデータを取り出すことによって、画面表示の速度を上げたいです。

改修

とりあえずAPI側でpageを引数に取ります。
これが分割の単位で、Flutterで取得する度に1からインクリメントされていきます。

def find_by_user(user_id: str, page: int, limit: int = 4):
    sakes = # お酒一覧を取得(省略)
    start_index = (page - 1) * limit
    end_index = start_index + limit
    return sakes[start_index:end_index]

ここからFlutterの処理

class {
    int _currentPage = 1; // 現在のページ番号
    bool _isLoading = false; // データ読み込み中かどうか
    bool _hasMoreData = true; // 残りの表示データがあるか
    final ScrollController _scrollController = ScrollController();

    
    void initState() {
        super.initState();
        _scrollController.addListener(_onScroll); // スクロールイベントの監視
    }

    
    void dispose() {
        _scrollController.removeListener(_onScroll); // スクロールイベントの監視を解除
        _scrollController.dispose();
        super.dispose();
    }

    void _onScroll() {
        if (_scrollController.position.pixels ==
            _scrollController.position.maxScrollExtent) {
            _loadMoreData(); // 一番下までスクロールしたらデータを読み込む
        }
    }

    Future<void> _loadMoreData() async {
        if (_isLoading || !_hasMoreData) {
          return; // 読み込み中またはデータがない場合は何もしない
        }
        setState(() {
          _isLoading = true; // 読み込み中フラグを立てる
        });
        _currentPage++; // ページ番号を増やす
        final newData = await sakeService.getSakes(_currentPage); // データを取得
        if (newData.isEmpty) {
          setState(() {
            _hasMoreData = false; // データがなければ、もうデータがないフラグを立てる
          });
        }
        setState(() {
          _showItems.addAll(newData); // 取得したデータを表示しているリストに追加
          _isLoading = false; // 読み込み中フラグを下ろす
        });
    }

    
    Widget build(BuildContext context) {
        // ~~省略~~
        child: CupertinoScrollbar(
            controller: _scrollController,
                child: CustomScrollView(
                    controller: _scrollController,
                    slivers: <Widget> [] // 表示処理(省略)
                ),
         ),
    }
}

Discussion