1. HOME
  2. ブログ
  3. エンジニアリング
  4. Flutterで無限スクロールを手早く実装する方法

BLOG

ブログ

エンジニアリング

Flutterで無限スクロールを手早く実装する方法

今回は、Flutterで無限スクロールを実装していきます。

Infinite Scroll Paginationを使います。

全体のソースコードはこちら

Infinite Scroll Paginationインストール

以下のコマンドでインストールします。

flutter pub add infinite_scroll_pagination

モックAPIを作成

以下のようなJSONデータを想定します。

{
   "total": 72,
   "per_page": 10,
   "current_page": 3,
   "last_page": 8,
   "from": 31,
   "to": 40,
   "data":[
        {
            // Record...
        },
        {
            // Record...
        }
   ]
}

以下のようなモックAPIを用意します。

import 'dart:convert';

final post = {
  'title': 'Flutterで無限スクロールを手早く実装する方法',
  'thumbnail':
      'https://techinit.co.jp/wp-content/uploads/2024/06/flutter-inifinite-scroll-pagination.png',
  'body': List.generate(10, (_) => 'ここにテキスト ').join(),
  'published_at': '2024/01/01 19:24',
  'updated_at': '2024/07/01 20:48',
};

Future<String> postsRes({required int page}) async {
  await Future.delayed(const Duration(seconds: 2));
  const total = 72;
  const perPage = 10;
  final posts = List.generate(total, (_) => post);
  return jsonEncode({
    'total': total,
    'per_page': perPage,
    'current_page': page,
    'last_page': (total / perPage).ceil().toInt(),
    'from': 10 * (page - 1) + 1,
    'to': 40,
    'data': posts.skip((page - 1) * 10).take(10).toList(),
  });
}

無限スクロール実装

画面表示部分は以下のようになります。

import 'package:flutter/material.dart';
import 'package:flutter_infinite_scroll_example/api.dart';
import 'package:flutter_infinite_scroll_example/models/post/post.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: _MyHomePage(),
    );
  }
}

class _MyHomePage extends StatefulWidget {
  const _MyHomePage();

  @override
  State<_MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<_MyHomePage> {
  static const _pageSize = 10;
  final PagingController<int, Post> _pagingController =
      PagingController(firstPageKey: 1);

  @override
  void initState() {
    super.initState();
    _pagingController.addPageRequestListener((pageKey) {
      _fetchPage(pageKey);
    });
  }

  Future<void> _fetchPage(int pageKey) async {
    try {
      final newItems = await getPosts(page: pageKey);
      final isLastPage = newItems.data.length < _pageSize;
      if (isLastPage) {
        _pagingController.appendLastPage(newItems.data);
      } else {
        final nextPageKey = pageKey + 1;
        _pagingController.appendPage(newItems.data, nextPageKey);
      }
    } catch (error) {
      _pagingController.error = error;
    }
  }

  @override
  void dispose() {
    _pagingController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('記事一覧'),
      ),
      body: PagedListView<int, Post>(
        pagingController: _pagingController,
        builderDelegate: PagedChildBuilderDelegate<Post>(
          itemBuilder: (context, item, index) => _PostView(post: item),
        ),
      ),
    );
  }
}

class _PostView extends StatelessWidget {
  final Post post;
  const _PostView({required this.post});

  @override
  Widget build(BuildContext context) {
    return ListTile(
        leading: Image.network(post.thumbnail),
        title: Text(post.title),
        subtitle: Text(post.published_at));
  }
}
  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

関連記事