【Flutter入門】ListViewの使い方と動的なリストを作る方法

Flutter

ListViewとは?

ListViewは、縦方向にスクロール可能なリストを表示するウィジェットです。様々なタイプのウィジェットを使用してリストを表示できますが、一般的に、ListTileを小要素として指定して、リストアイテムを作成することが多いです。

ListTileは、アイコン、テキスト、サブテキストなどを含む簡単なリストアイテムを表示するのに便利なウィジェットです。以下はListTileを使った、ListViewの単純な例です。

main.dart

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

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

class MainApp extends StatelessWidget {
  const MainApp({Key? key}) : super(key: key); 

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

main_list.dart

import 'package:flutter/material.dart';

class MainListWidget extends StatelessWidget {
  const MainListWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        ListTile(
          leading: const Icon(Icons.access_alarm),
          title: const Text('Alarm'),
          subtitle: const Text('Alarm description'),
          onTap: () {
            print('Alarm was tapped!');
          }
        ),
        ListTile(
          leading: const Icon(Icons.account_box),
          title: const Text('Profile'),
          subtitle: const Text('Profile description'),
          onTap: () {
            print('Profile was tapped!');
          }
        ),
        ListTile(
          leading: const Icon(Icons.camera_alt),
          title: const Text('Camera'),
          subtitle: const Text('Camera description'),
          onTap: () {
            print('Camera was tapped!');
          }
        ),
      ],
    );
  }
}

動的なリストの作成方法

実際にリストを作成する場合は、リストのアイテムを全てハードコードするのではなく、DBなどから取得したデータをループでリストに出力するということが多いでしょう。

リストアイテムを動的に生成するには、リストを作成してからListView.builderメソッドを使います。
itemsというリストを作成し、ListView.builderメソッドを使ってListViewに表示してみましょう。

ListView.builderに必要なのは、itemCountとitemBuilderです。itemCountにはリストのアイテムの数を渡します。
itemBuilderは各アイテムを構築するための関数です。
BuildContext オブジェクトとリストアイテムのインデックスの2つの引数を取り、ビルドするリストアイテムのウィジェットを返します。

import 'package:flutter/material.dart';

class MainListWidget extends StatelessWidget {
  const MainListWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final List<Map<String, dynamic>> items = [
      {'icon': Icons.access_alarm, 'title': 'Alarm', 'subtitle': 'Alarm description'},
      {'icon': Icons.account_box, 'title': 'Profile', 'subtitle': 'Profile description'},
      {'icon': Icons.camera_alt, 'title': 'Camera', 'subtitle': 'Camera description'},
    ];

    return ListView.builder(
      itemCount: items.length,
      itemBuilder: (BuildContext context, int index) {
        final item = items[index];
        return ListTile(
          leading: Icon(item['icon'] as IconData),
          title: Text(item['title'] as String),
          subtitle: Text(item['subtitle'] as String),
          onTap: () {
            print('${item['title']} was tapped!');
          },
        );
      }
    );
  }
}

DBから取得したデータをリスト表示する

データベースからデータを取得する処理は非同期で行われますが、データが取得される前にリストを構築しようとするとエラーが発生します。つまり、データ取得後にリストの構築がされるように実装しなければなりません。

そこで使用するのがFutureBuilderです。
FutureBuilderは非同期に取得されるデータをリストビューに表示するために使うウィジェットで、処理が完了するまで待ち、データが利用可能になると自動的にリストビューをします。
簡単にいうと、FutureBuilderを使うことで、データが取得されるまで待ち、取得されたらリストを自動的に構築するということができます。

FutureBuilderには以下の2つのパラメータが必要です。
・future: データを取得する非同期処理関数を指定
・builder: 取得したデータを元に、表示するウィジェットを構築する関数を指定

builder プロパティは、AsyncSnapshot オブジェクトを受け取るのですが、これが重要な役割を持っています。
AsyncSnapshot には、非同期処理の結果や状態が格納されいるので、AsyncSnapshot オブジェクトを使用して非同期処理の状態に応じた処理を書くことができます。

具体的には、snapshot.hasDataで非同期処理で取得したデータがあるかどうかを判断し、データがなければCircularProgressIndicator() で待機中のグルグルを表示するといったことができます。正常にデータが取得できていれば、snapshot.dataからデータを取得しリストを構築します。

import 'package:flutter/material.dart';

Future<List<Map<String, dynamic>>> getItems() async{
  await Future.delayed(const Duration(seconds: 2));
  return [
      {'icon': Icons.access_alarm, 'title': 'Alarm', 'subtitle': 'Alarm description'},
      {'icon': Icons.account_box, 'title': 'Profile', 'subtitle': 'Profile description'},
      {'icon': Icons.camera_alt, 'title': 'Camera', 'subtitle': 'Camera description'},
    ];
}

class MainListWidget extends StatelessWidget {
  const MainListWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Map<String, dynamic>>>(
      future: getItems(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final items = snapshot.data!;
          return ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              final item = items[index];
              return ListTile(
                leading: Icon(item['icon'] as IconData),
                title: Text(item['title'] as String),
                subtitle: Text(item['subtitle'] as String),
                onTap: () {
                  print('${item['title']} was tapped!');
                },
              );
            },
          );
        } else {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
      },
    );
  }
}

getItems関数がDBなどから非同期でデータを取得するダミー関数です。アプリを起動すると2秒読み込み表示がされた後にリストのデータが表示されます。

タイトルとURLをコピーしました