Flutterで無限スクロールリストを作成する方法

今回はListViewウィジェットを利用し、スクロールすると無限に文字列が湧き出てくるアプリを作ってみます。

前回の記事で学んだStatefulWidgetを使って進めますので、StatefulWidgetについて詳しく学びたい方はこちらも参考にしてみてください。

【初心者向け】Flutterアプリを作ってStatefulWidgetを学ぶ

※今回のアプリはFlutter公式サイトのチュートリアルを参考にしています

サンプルアプリを作ってみる

アプリの概要

次のステップでアプリを作ってみます。

  • ランダム文字列を生成するライブラリをインストール
  • StatefulWidgetを継承したRandomWordsクラスを作成する
  • ランダム文字列を生成・表示する_buildSuggestionsメソッドを追加

実装

文字列生成用のライブラリをインストール

はじめにランダム文字列を生成するenglish_wordsをインストールします。


# pubspec.yamlに追記
dependencies:
  flutter:
    sdk: flutter
  english_words: ^3.1.5 # 追記

// main.dartに追記
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart'; // 追記

RandomWordsクラスを作成

次にランダム文字列のリストを表示するWidget、RandomWordsクラスを作るためにStatefulWidgetの雛形を作成します。


class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

生成した文字列を格納するリスト_suggestionsとフォントサイズの定義した_biggerFont変数を予め用意しておきます。


class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = TextStyle(fontSize: 18.0);
  @override
  // ・・・
}

ランダム文字列を生成・表示する_buildSuggestionsメソッドを追加

次にListViewウィジェットでランダム文字列のリストを表示する_buildSuggestions()メソッドを追加します。


Widget _buildSuggestions() {
  return ListView.builder(
      padding: EdgeInsets.all(16.0),
      itemBuilder: (context, i) {
        if (i.isOdd) return Divider();

        final index = i ~/ 2;
        if (index >= _suggestions.length) {
          _suggestions.addAll((generateWordPairs().take(10)));
        }
        return _buildRow(_suggestions[index]);
      });
}

※この状態ではThe method ‘_buildRow’ isn’t defined for the class ‘_RandomWordsState’.とエラーが表示します

ListViewは無名関数として指定されたファクトリービルダー及びコールバック関数であるビルダープロパティitemBuilderを提供します。

無名関数には2つのパラメータ、BuildContextであるcontext行のイテレータiが渡されます。

ListView class – widgets library – Dart API

itemBuilderコールバックは、各行ごとに1回呼び出され、指定された要素がListTileという行に配置されます。

今回のケースでは偶数行は、生成されたランダム文字列を、奇数行の場合は、分割ウィジェット(水平線)が追加され、視覚的に文字列を分離します。

各処理の詳細を見ていきましょう。

itemBuilderの1行目if (i.isOdd) return Divider();はその行が奇数であるか判定し、奇数の場合はDividerウィジェットで水平線を返します。

Divider class – material library – Dart API

偶数業の場合は次のステップへ。

final index = i ~/ 2;で水平線の数を差し引き、ランダム文字列の実際の数を算出しています。

さらに_suggetionsに格納されている数より大きくなった場合に、10個のランダム文字列を生成し追加します。

最後に_buildRow()メソッドを呼び出し、新しいランダム文字列をListTileに配置します。

ListTileを返す_buildRowメソッドを_RandomWordStateに追加します。


Widget _buildRow(WordPair pair) {
  return ListTile(
    title: Text(
      pair.asPascalCase,
      style: _biggerFont,
    ),
  );
}

準備を整いましたので、仕上げです。

_RandomWordsStateクラスのbuild()を次のように更新します。


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Infinity Scroll ListView'),
    ),
    body: _buildSuggestions(),
  );
}

最後にMyAppクラスを更新します。


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Infinite Scroll ListView',
      home: RandomWords(),
    );
  }
}

これで全ての実装が完了しました。

以下が完成したmain.dartの全文です。


import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Infinite Scroll ListView',
      home: RandomWords(),
    );
  }
}

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Infinite Scroll ListView'),
      ),
      body: _buildSuggestions(),
    );
  }

  Widget _buildSuggestions() {
    return ListView.builder(
        padding: EdgeInsets.all(16.0),
        itemBuilder: (context, i) {
          if (i.isOdd) return Divider();

          final index = i ~/ 2;
          if (index >= _suggestions.length) {
            _suggestions.addAll((generateWordPairs().take(10)));
          }
          return _buildRow(_suggestions[index]);
        });
  }

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }
}

実行

作成したアプリを実行してみましょう。

スクロールするたびに生成されたランダム文字列が無限に湧き出てきたら成功です。

ABOUTこの記事をかいた人

Yusuke Ito

■バックエンド開発(TypeScript、PHP etc)をメインに活動し、2020年からFlutterによるアプリ開発案件に参画し、多様なニーズに応えれるエンジニアを目指す。 ■エンジニア目線でより良い提案、目的に合わせた仕様の提案が得意です。 ■Flutterに関する技術、個人アプリの開発、キャリア、書評をメインに発信しています。 ■筋トレ、ゴルフ、読書が趣味