0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Flutter]初心者でもできる無限スクロールの実装方法

Posted at

一覧表示画面で、スクロールすると次々にデータを読み込んでいく、無限スクロールを簡単に作る方法を紹介します。
無限スクロールを作る方法がいろいろあるのですが、初心者の自分でもできたのでこれが一番簡単かと思います。

データを取得するAPIを準備する

今回はAPI経由でデータを取得し、一覧表示するのでまずAPIを準備します。
今回は、以下のswaggerで記載したtitleのみが一覧で返されるAPIを用意しました。次のデータがあれば、レスポンスのoffsetに次の位置が返り、なければ返りません。

sample.yml
openapi: 3.0.0
info:
  title: sample
  version: '1.0'
  description: sample
  contact:
    name: murapon
servers:
  - url: 'http://localhost:31180'
paths:
  /list:
    get:
      operationId: get-list
      summary: 一覧取得
      description: 一覧取得
      parameters:
        - schema:
            type: integer
            default: '10'
          in: query
          name: limit
          description: limit
        - schema:
            type: integer
            default: '0'
          in: query
          name: offset
          description: offset
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/response_list_get'
components:
  schemas:
    response_list_get:
      title: reponse_list_get
      description: 一覧取得APIのレスポンスモデル
      type: object
      properties:
        list:
          type: array
          items:
            type: object
            properties:
              title:
                type: string
                description: タイトル
            required:
              - title
        total_count:
          type: integer
          description: 総件数
        next_offset:
          type: integer
          description: 次のoffset
      required:
        - list
        - total_count

flutterでswaggerを読み込んで、APIからデータを取得できるようにする

FlutterでSwagger(openapi-generator)を使う方法を参考にしてください。

一覧表示を作る

以下が、flutter側の全ソースです。

sample_page.dart
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:openapi/api.dart';

class SamplePage extends StatefulWidget {
  const SamplePage({
    Key key,
  }) : super(key: key);

  @override
  _SamplePageState createState() => _SamplePageState();
}

class _SamplePageState extends State<SamplePage> {
  List<Map> list = null;
  bool loading = false;
  int offset = 0;

  @override
  Widget build(BuildContext context) {
    var length = list?.length ?? 0;
    return Scaffold(
      appBar: AppBar(title: Text("一覧")),
      body: new ListView.builder(
        itemBuilder: (context, index) {
          if (index == length && offset!=null) {
            _load();
            return new Center(
              child: new Container(
                margin: const EdgeInsets.only(top: 8.0),
                width: 32.0,
                height: 32.0,
                child: const CircularProgressIndicator(),
              ),
            );
          } else if (index > length) {
            return null;
          }
          if(list.length > index) {
            var event = list[index];
            return Container(
              child: ListTile(title: Text(event['title'])),
            );
          } else {
            return null;
          }
        },
      ),
    );
  }

  Future<void> _load() async {
    if (loading || offset == null) {
      return null;
    }
    loading = true;
    try {
      var client = ApiClient(basePath: "http://10.0.2.2:31180");
      var defaultApi = DefaultApi(client);
      int limit = 3; // 一度に取得する件数
      var response = await defaultApi.getListWithHttpInfo(
          limit:limit, offset: offset);
      ResponseListGet results =  ResponseListGet.fromJson(jsonDecode(response.body));
      setState(() {
        offset = results.nextOffset;
        if (list == null) {
          list = <Map>[];
        }
        results.list.forEach((dynamic item) {
          list
              .add({'title': item.title as String});
        });
      });

    } catch (e) {
      print(e.toString());
    } finally {
      loading = false;
    }
  }
}

最初の、

sample_page.dart(抜粋)
List<Map> list = null;
bool loading = false;
int offset = 0;

で、初期化し、_load();を実行します。
_load();の中で、APIからデータを取得し、setStateを使って、取得したデータをlistに入れつつ、再描画処理を実行し、初期表示を行います。
2回目は、取得したoffsetの値があれば、再度APIを実行しなければ、実行しません。
API経由で全データを取得し終わったら、それ以上スクロールしても何もしないという実装にしたかったので、loadingoffsetで制御しましたが、これを二つを消せば、何度も_load()を実行し続けることが可能です。_load()の中身を変えることで、全部表示したらまた最初から表示し直すなどの表示も可能です。

0
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
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?