🐰

【Flutter】ListViewの縦方向表示アイテム数を指定する

2024/01/16に公開

はじめに

ListViewを使いたいけど、縦方向に表示されるアイテム(Widget)数はxxだけにしたい」という時がありますよね?

何を言っているかというと、縦方向のアイテム数を5に設定すると、以下のように表示されて

以下のようにウィンドウのサイズを変更してもアイテム数は5のままなListViewを使いたいというわけです。

(スクロール中は見切れたアイテムが表示されるので正確には6アイテム見えるのですが。)

こういうときありますよね?(ないか)

これを実現してくれる方法を見つけられなかったので、Widgetを自作しました。

自作したWidget

class ListViewWithViewItemCount extends StatelessWidget {
  const ListViewWithViewItemCount({
    super.key,
    required this.viewItemCount,
    required this.children,
  });
  final int viewItemCount;
  final List<Widget> children;

  
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return ListView(
          itemExtent: constraints.maxHeight / viewItemCount,
          children: children,
        );
      },
    );
  }
}

LayoutBuilderを用いることでListViewの親Widgetのconstraintsを知ることができます。
constraints.maxHeightと表示アイテム数から、1アイテムの高さを計算し、ListViewitemExtentに指定します。
これで、ListViewの1スクロール画面内には指定した個数のアイテムだけが表示されます。

使用例

以下の使用例は冒頭に載せた画面のアプリケーションの実装です。

lib/main.dart
import 'package:flutter/material.dart';

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

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
+        child: ListViewWithViewItemCount(
+          viewItemCount: 5,
+          children: [
+            for (final i in List.generate(30, (index) => index))
+            ListTile(
+              title: Text('item$i'),
+              subtitle: Text('subtitile$i'),
+              leading: const Icon(Icons.settings),
+              trailing: const Icon(Icons.arrow_forward),
+            ),
+          ],
+        ),
      ),
    );
  }
}

おわりに

LayoutBuilderを使ったことがなかったため、実現方法が全くわからず四苦八苦しました。
(Rendering中、Widgetのサイズ変更を通知させて動的にアイテムのサイズを変更して例外発生したり。)

他にもっと良い方法をご存じの方はぜひ教えてください。。。

参考文献

https://stackoverflow.com/questions/41558368/how-can-i-layout-widgets-based-on-the-size-of-the-parent

迷走時

  • Rendering時のサイズを知ることでアイテムの高さを指定しようとしていたとき

https://torikatsu923.hatenablog.com/entry/2021/07/31/000637

https://zenn.dev/s134/articles/20231119rendering_size

https://stackoverflow.com/questions/76807611/how-can-i-create-a-flutter-widget-that-detects-size-changes-similar-to-how-a-re

Discussion