Flutter ListView とスクロール
ListView は Flutter で最もよく使われるスクロールリストウィジェットで、スクロール可能なコンテンツリストを表示するために使用されます。
ListView の基本用法
例:ListView の基本用法
// 方法1: children パラメータ(少量の項目に適しています)
ListView(
children: [
ListTile(title: Text('第1項')),
ListTile(title: Text('第2項')),
ListTile(title: Text('第3項')),
],
)
// 方法2: ListView.builder(大量または無限リストに適しています)
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
)
// 方法3: ListView.separated(セパレータ付き)
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(), // 区切り線
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
)
ListView(
children: [
ListTile(title: Text('第1項')),
ListTile(title: Text('第2項')),
ListTile(title: Text('第3項')),
],
)
// 方法2: ListView.builder(大量または無限リストに適しています)
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
)
// 方法3: ListView.separated(セパレータ付き)
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(), // 区切り線
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
)
ListView のよく使う属性
| 属性 | 説明 |
|---|---|
| scrollDirection | スクロール方向。デフォルトは Axis.vertical(垂直) |
| reverse | 逆方向にスクロールするかどうか |
| padding | リストのパディング |
| itemExtent | 項目の高さを固定し、パフォーマンスを向上させます |
| prototypeItem | プロトタイプ項目。高さの計算に使用します |
例:横方向の ListView
// 横スクロールリスト
SizedBox(
height: 120, // 高さを設定する必要があります
child: ListView.builder(
scrollDirection: Axis.horizontal, // 横スクロール
itemCount: 20,
itemBuilder: (context, index) {
return Container(
width: 100,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Colors.blue[100],
child: Center(
child: Text('項目 $index'),
),
);
},
),
)
SizedBox(
height: 120, // 高さを設定する必要があります
child: ListView.builder(
scrollDirection: Axis.horizontal, // 横スクロール
itemCount: 20,
itemBuilder: (context, index) {
return Container(
width: 100,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Colors.blue[100],
child: Center(
child: Text('項目 $index'),
),
);
},
),
)
GridView - グリッドレイアウト
GridView は二次元のグリッドリストを作成するために使用されます。
例:GridView の使い方
// GridView.count - 固定の列数
GridView.count(
crossAxisCount: 3, // 3 列
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.blue[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
// GridView.builder - 動的に構築
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 4 列
mainAxisSpacing: 8, // 垂直方向の間隔
crossAxisSpacing: 8, // 水平方向の間隔
childAspectRatio: 1, // アスペクト比
),
itemCount: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.green[100],
child: Center(child: Text('${index + 1}')),
);
},
)
// GridView.extent - 自動適応列数
GridView.extent(
maxCrossAxisExtent: 150, // 最大列幅
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.orange[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
GridView.count(
crossAxisCount: 3, // 3 列
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.blue[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
// GridView.builder - 動的に構築
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 4 列
mainAxisSpacing: 8, // 垂直方向の間隔
crossAxisSpacing: 8, // 水平方向の間隔
childAspectRatio: 1, // アスペクト比
),
itemCount: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.green[100],
child: Center(child: Text('${index + 1}')),
);
},
)
// GridView.extent - 自動適応列数
GridView.extent(
maxCrossAxisExtent: 150, // 最大列幅
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.orange[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
スクロール制御
ScrollController を使用してスクロール位置を制御できます。
例:スクロール制御
class ScrollExample extends StatefulWidget {
const ScrollExample({super.key});
@override
State<ScrollExample> createState() => _ScrollExampleState();
}
class _ScrollExampleState extends State<ScrollExample> {
// スクロールコントローラーを作成
late ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
// スクロールイベントを監視
_controller.addListener(() {
print('スクロール位置: ${_controller.offset}');
});
}
@override
void dispose() {
// コントローラーを破棄
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// トップへスクロールボタン
ElevatedButton(
onPressed: () {
// トップへスクロール
_controller.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: const Text('トップへスクロール'),
),
// リスト
Expanded(
child: ListView.builder(
controller: _controller, // コントローラーをバインド
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
),
),
],
);
}
}
const ScrollExample({super.key});
@override
State<ScrollExample> createState() => _ScrollExampleState();
}
class _ScrollExampleState extends State<ScrollExample> {
// スクロールコントローラーを作成
late ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
// スクロールイベントを監視
_controller.addListener(() {
print('スクロール位置: ${_controller.offset}');
});
}
@override
void dispose() {
// コントローラーを破棄
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// トップへスクロールボタン
ElevatedButton(
onPressed: () {
// トップへスクロール
_controller.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: const Text('トップへスクロール'),
),
// リスト
Expanded(
child: ListView.builder(
controller: _controller, // コントローラーをバインド
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('第 ${index + 1} 項'));
},
),
),
],
);
}
}
Slivers 高度なスクロール
CustomScrollView と Slivers を組み合わせることで、より複雑なスクロール効果を実現できます。
例:Slivers での折りたたみ効果
// 折りたたみアプリバー付きスクロールビュー
CustomScrollView(
slivers: [
// 折りたたみアプリバー
SliverAppBar(
expandedHeight: 200, // 展開時の高さ
pinned: true, // 上部に固定
flexibleSpace: FlexibleSpaceBar(
title: const Text('折りたたみタイトル'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
),
),
// リスト内容
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('第 ${index + 1} 項')),
childCount: 30,
),
),
],
)
// グリッド Sliver
CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('画像グリッド'),
pinned: true,
),
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
delegate: SliverChildBuilderDelegate(
(context, index) => Card(
child: Image.network('https://picsum.photos/200?$index'),
),
childCount: 20,
),
),
],
)
CustomScrollView(
slivers: [
// 折りたたみアプリバー
SliverAppBar(
expandedHeight: 200, // 展開時の高さ
pinned: true, // 上部に固定
flexibleSpace: FlexibleSpaceBar(
title: const Text('折りたたみタイトル'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
),
),
// リスト内容
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('第 ${index + 1} 項')),
childCount: 30,
),
),
],
)
// グリッド Sliver
CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('画像グリッド'),
pinned: true,
),
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
delegate: SliverChildBuilderDelegate(
(context, index) => Card(
child: Image.network('https://picsum.photos/200?$index'),
),
childCount: 20,
),
),
],
)
その他の拡張Slivers は、多数のスクロール項目を処理するための効率的な方法を提供します。可視領域の項目のみをレンダリングするため、優れたパフォーマンスを提供します。