Flutter ListView and Scrolling
ListView is the most commonly used scrolling list Widget in Flutter, used to display scrollable content lists.
ListView Basic Usage
Example: ListView Basic Usage
// Method 1: children parameter (suitable for a small number of items)
ListView(
children: [
ListTile(title: Text('First item')),
ListTile(title: Text('Second item')),
ListTile(title: Text('Third item')),
],
)
// Method 2: ListView.builder (suitable for large or infinite lists)
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
)
// Method 3: ListView.separated (with separators)
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(), // Divider
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
)
ListView(
children: [
ListTile(title: Text('First item')),
ListTile(title: Text('Second item')),
ListTile(title: Text('Third item')),
],
)
// Method 2: ListView.builder (suitable for large or infinite lists)
ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
)
// Method 3: ListView.separated (with separators)
ListView.separated(
itemCount: 10,
separatorBuilder: (context, index) => const Divider(), // Divider
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
)
ListView Common Properties
| Property | Description |
|---|---|
| scrollDirection | Scroll direction, defaults to Axis.vertical (vertical). |
| reverse | Whether to reverse scroll |
| padding | List padding |
| itemExtent | Fixed Item Height for Better Performance |
| prototypeItem | Prototype Item for Calculating Height |
Example: Horizontal ListView
// Horizontal scrolling list
SizedBox(
height: 120, // Must set height
child: ListView.builder(
scrollDirection: Axis.horizontal, // Horizontal scrolling
itemCount: 20,
itemBuilder: (context, index) {
return Container(
width: 100,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Colors.blue[100],
child: Center(
child: Text('Item $index'),
),
);
},
),
)
SizedBox(
height: 120, // Must set height
child: ListView.builder(
scrollDirection: Axis.horizontal, // Horizontal scrolling
itemCount: 20,
itemBuilder: (context, index) {
return Container(
width: 100,
margin: const EdgeInsets.symmetric(horizontal: 8),
color: Colors.blue[100],
child: Center(
child: Text('Item $index'),
),
);
},
),
)
GridView - Grid Layout
GridView is used to create two-dimensional grid lists.
Example: GridView Usage
// GridView.count - fixed number of columns
GridView.count(
crossAxisCount: 3, // 3 columns
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.blue[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
// GridView.builder - dynamic construction
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 4 columns
mainAxisSpacing: 8, // Vertical spacing
crossAxisSpacing: 8, // Horizontal spacing
childAspectRatio: 1, // Aspect ratio
),
itemCount: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.green[100],
child: Center(child: Text('${index + 1}')),
);
},
)
// GridView.extent - adaptive column count
GridView.extent(
maxCrossAxisExtent: 150, // Maximum column width
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 columns
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.blue[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
// GridView.builder - dynamic construction
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 4, // 4 columns
mainAxisSpacing: 8, // Vertical spacing
crossAxisSpacing: 8, // Horizontal spacing
childAspectRatio: 1, // Aspect ratio
),
itemCount: 50,
itemBuilder: (context, index) {
return Container(
color: Colors.green[100],
child: Center(child: Text('${index + 1}')),
);
},
)
// GridView.extent - adaptive column count
GridView.extent(
maxCrossAxisExtent: 150, // Maximum column width
children: List.generate(20, (index) {
return Container(
margin: const EdgeInsets.all(4),
color: Colors.orange[100],
child: Center(child: Text('${index + 1}')),
);
}),
)
Scroll control
Using ScrollController you can control the scroll position.
Example: Scroll Control
class ScrollExample extends StatefulWidget {
const ScrollExample({super.key});
@override
State<ScrollExample> createState() => _ScrollExampleState();
}
class _ScrollExampleState extends State<ScrollExample> {
// Create scroll controller
late ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
// Listen for scroll events
_controller.addListener(() {
print('Scroll position: ${_controller.offset});
});
}
@override
void dispose() {
// Dispose controller
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// Scroll to top button
ElevatedButton(
onPressed: () {
// Scroll to top
_controller.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: const Text('Scroll to top'),
),
// List
Expanded(
child: ListView.builder(
controller: _controller, // Bind controller
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
),
),
],
);
}
}
const ScrollExample({super.key});
@override
State<ScrollExample> createState() => _ScrollExampleState();
}
class _ScrollExampleState extends State<ScrollExample> {
// Create scroll controller
late ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
// Listen for scroll events
_controller.addListener(() {
print('Scroll position: ${_controller.offset});
});
}
@override
void dispose() {
// Dispose controller
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// Scroll to top button
ElevatedButton(
onPressed: () {
// Scroll to top
_controller.animateTo(
0,
duration: const Duration(milliseconds: 500),
curve: Curves.easeInOut,
);
},
child: const Text('Scroll to top'),
),
// List
Expanded(
child: ListView.builder(
controller: _controller, // Bind controller
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Item ${index + 1}'));
},
),
),
],
);
}
}
Slivers Advanced Scrolling
CustomScrollView combined with Slivers can achieve more complex scrolling effects.
Example: Slivers Collapsing Effect
// Scroll view with collapsible app bar
CustomScrollView(
slivers: [
// Collapse app bar
SliverAppBar(
expandedHeight: 200, // Expanded height
pinned: true, // Fixed at top
flexibleSpace: FlexibleSpaceBar(
title: const Text('Collapsible title'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
),
),
// List content
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item ${index + 1}')),
childCount: 30,
),
),
],
)
// Grid Sliver
CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('Image Grid'),
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: [
// Collapse app bar
SliverAppBar(
expandedHeight: 200, // Expanded height
pinned: true, // Fixed at top
flexibleSpace: FlexibleSpaceBar(
title: const Text('Collapsible title'),
background: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.purple],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
),
),
// List content
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => ListTile(title: Text('Item ${index + 1}')),
childCount: 30,
),
),
],
)
// Grid Sliver
CustomScrollView(
slivers: [
const SliverAppBar(
title: Text('Image Grid'),
pinned: true,
),
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
delegate: SliverChildBuilderDelegate(
(context, index) => Card(
child: Image.network('https://picsum.photos/200?$index'),
),
childCount: 20,
),
),
],
)
other extensionsSlivers provide an efficient way to handle a large number of scrolling items; they only render items in the visible area, thus providing good performance.