Flutter ListView and GridView
Almost every app shows a collection: chat messages, products, photos, settings. Flutter provides ListView for items in a single scrolling line and GridView for items arranged in rows and columns. Both widgets scroll automatically when content exceeds the screen.
The Simplest ListView
Pass a list of widgets to children. Flutter builds all of them at once.
ListView(
children: [
ListTile(title: Text('Apple')),
ListTile(title: Text('Banana')),
ListTile(title: Text('Cherry')),
],
)
This style suits short lists of a dozen items or fewer.
ListTile
ListTile is a ready-made row with room for a leading icon, a title, a subtitle, and a trailing widget.
ListTile(
leading: Icon(Icons.person),
title: Text('Asha'),
subtitle: Text('Online'),
trailing: Icon(Icons.chevron_right),
onTap: () {
print('Tapped Asha');
},
)
┌──────────────────────────────────────────┐
│ (icon) Title > │
│ Subtitle │
└──────────────────────────────────────────┘
leading title / subtitle trailing
ListView.builder for Long Lists
A list with ten thousand items would freeze the app if Flutter built every row at once. ListView.builder creates only the rows visible on screen and recycles them as the user scrolls. Picture a conveyor belt at a sushi bar: the kitchen places only the plates within reach of the guests.
final List<String> names = ['Asha', 'Ravi', 'Meera', 'Kabir'];
ListView.builder(
itemCount: names.length,
itemBuilder: (context, index) {
return ListTile(title: Text(names[index]));
},
)
Data list (10,000 items) Screen shows ~8 rows
┌────────────────────┐ ┌────────────────┐
│ item 0 │ │ item 42 │
│ item 1 │ build │ item 43 │
│ ... │ ──────→ │ item 44 │
│ item 9999 │ only │ ... │
└────────────────────┘ these └────────────────┘
The itemCount value tells Flutter where the list ends. The itemBuilder function receives the position and returns the widget for that position.
Adding Dividers
Use ListView.separated to place a line between rows.
ListView.separated(
itemCount: names.length,
separatorBuilder: (context, index) {
return Divider();
},
itemBuilder: (context, index) {
return ListTile(title: Text(names[index]));
},
)
Horizontal Lists
Set scrollDirection to Axis.horizontal for a sideways carousel. Give the list a fixed height with SizedBox, because a horizontal list inside a column has no height limit otherwise.
SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (context, index) {
return Container(width: 100, color: Colors.teal, margin: EdgeInsets.all(8));
},
),
)
GridView
A grid arranges items in columns. The quickest way uses GridView.count with a fixed number of columns.
GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
children: List.generate(9, (index) {
return Container(color: Colors.indigo, child: Center(child: Text('$index')));
}),
)
┌─────┐ ┌─────┐ ┌─────┐
│ 0 │ │ 1 │ │ 2 │
└─────┘ └─────┘ └─────┘
┌─────┐ ┌─────┐ ┌─────┐
│ 3 │ │ 4 │ │ 5 │
└─────┘ └─────┘ └─────┘
crossAxisCount: 3
GridView.builder
Large grids such as photo galleries need lazy building too. GridView.builder works like ListView.builder and adds a gridDelegate that sets the column rules.
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.2,
),
itemCount: 100,
itemBuilder: (context, index) {
return Card(child: Center(child: Text('Item $index')));
},
)
Use SliverGridDelegateWithMaxCrossAxisExtent when you prefer to set a maximum tile width and let Flutter calculate the column count. This choice adapts well to wide screens.
Scroll Controls
shrinkWrap
A list placed inside a Column throws a height error. Setting shrinkWrap: true makes the list take only the space its items need. Wrap a long list in Expanded instead, because shrinkWrap forces Flutter to measure every item.
physics
Set physics: NeverScrollableScrollPhysics() to disable scrolling for a list nested inside another scroll view.
ScrollController
A ScrollController reads the scroll position and jumps to a spot, such as the top of the list.
Which Widget to Choose
| Need | Widget |
|---|---|
| Few fixed rows | ListView with children |
| Many or unknown rows | ListView.builder |
| Rows with dividers | ListView.separated |
| Fixed column grid | GridView.count |
| Large grid | GridView.builder |
Pull to Refresh
Many apps let users drag a list downward to reload data. Wrap a list in a RefreshIndicator and supply an async function. Flutter shows a spinner while the function runs and hides it when the function finishes.
Swipe to Delete
The Dismissible widget wraps a row and removes it when the user swipes. Give each row a unique key, so Flutter knows which row disappeared. Remove the matching item from your data list inside onDismissed, or the row returns on the next rebuild.
Practice Ideas
Build a contact list with ten names using ListView.builder and show an avatar letter in the leading slot of each tile. Convert the same data into a two-column grid of cards. Compare how each layout feels on a narrow phone and a wide tablet.
