Open2

ListViewとは?

muranakarmuranakar

1. ListViewとは

ListViewは Flutter で最も一般的に使用されるスクロール可能なウィジェットです。子ウィジェットを線形に配置し、スクロール方向に沿って順番に表示します。

主な特徴:

  • スクロール可能な要素を簡単に実装できる
  • 子ウィジェットは横方向または縦方向に配置可能
  • メモリ効率の良い遅延ロードをサポート
  • カスタマイズ性が高い

2. ListViewの4つの構築方法

2.1 デフォルトコンストラクタ

ListView(
  padding: const EdgeInsets.all(8),
  children: <Widget>[
    Container(
      height: 50,
      color: Colors.amber[600],
      child: const Center(child: Text('項目 A')),
    ),
    Container(
      height: 50, 
      color: Colors.amber[500],
      child: const Center(child: Text('項目 B')),
    ),
  ],
)

使用場面:

  • 少ない数の固定項目を表示する場合
  • すべての子ウィジェットを一度に構築する必要がある場合

2.2 ListView.builder

final List<String> items = <String>['A', 'B', 'C'];

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('項目 ${items[index]}'),
    );
  },
)

使用場面:

  • 大量のデータを表示する場合
  • 無限スクロールを実装する場合
  • メモリ効率を重視する場合

2.3 ListView.separated

ListView.separated(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('項目 ${items[index]}'),
    );
  },
  separatorBuilder: (context, index) => const Divider(),
)

使用場面:

  • リスト項目間に区切り線などを入れたい場合
  • 固定数の項目がある場合

2.4 ListView.custom

ListView.custom(
  childrenDelegate: SliverChildBuilderDelegate(
    (context, index) => ListTile(
      title: Text('項目 $index'),
    ),
  ),
)

使用場面:

  • より詳細なカスタマイズが必要な場合
  • 特殊なスクロール動作を実装する場合

3. 重要なプロパティ

3.1 基本プロパティ

ListView(
  scrollDirection: Axis.vertical,    // スクロール方向
  reverse: false,                    // リストの逆順表示
  controller: scrollController,      // スクロールコントローラー
  physics: ScrollPhysics(),          // スクロールの物理演算
  padding: EdgeInsets.all(8.0),      // パディング
)

3.2 パフォーマンス最適化プロパティ

ListView.builder(
  itemCount: items.length,
  cacheExtent: 100.0,              // キャッシュ範囲
  addAutomaticKeepAlives: true,    // 状態保持
  addRepaintBoundaries: true,      // 再描画の最適化
)

4. メモリ管理とライフサイクル

4.1 要素の作成

// 表示領域に入った時に要素が作成される
ListView.builder(
  itemBuilder: (context, index) {
    print('要素 $index を作成');
    return ListTile(title: Text('項目 $index'));
  },
)

4.2 要素の破棄

画面外にスクロールされた要素は自動的に破棄されます。ただし、以下の方法で状態を保持できます:

// KeepAliveを使用した状態保持
class MyListItem extends StatefulWidget {
  
  _MyListItemState createState() => _MyListItemState();
}

class _MyListItemState extends State<MyListItem> 
    with AutomaticKeepAliveClientMixin {
  
  bool get wantKeepAlive => true;  // 状態を保持

  
  Widget build(BuildContext context) {
    super.build(context);
    return ListTile(
      title: Text('保持される項目'),
    );
  }
}

5. エッジケースの処理

5.1 空のリストの処理

Widget build(BuildContext context) {
  return items.isEmpty 
    ? Center(child: Text('データがありません'))
    : ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(items[index]),
          );
        },
      );
}

5.2 スクロール位置の保持

ListView(
  key: PageStorageKey('listView'),  // スクロール位置を保持
  children: items.map((item) => ListTile(
    title: Text(item),
  )).toList(),
)

6. パフォーマンス最適化のベストプラクティス

  1. 適切なコンストラクタの選択
  2. キャッシュの活用
  3. 要素の再利用
  4. 画像の遅延ロード
  5. スクロール位置の最適化