ListView and GridView are both scrollable layout widgets built on the same underlying machinery, and share most of their constructors and performance characteristics. The difference is purely about arrangement: one column versus a grid.
ListView: One Item After Another
ListView.builder(
itemCount: messages.length,
itemBuilder: (context, index) {
return ListTile(title: Text(messages[index].text));
},
)
Use ListView.builder (not the plain ListView(children: [...]) constructor) for anything beyond a handful of items — it only builds the widgets currently visible on screen, rather than every item up front, which matters a lot for a list with hundreds or thousands of entries.
GridView: A Fixed Number of Columns
GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
itemCount: products.length,
itemBuilder: (context, index) {
return ProductCard(product: products[index]);
},
)
The gridDelegate is what makes GridView different from ListView — it decides how items are arranged into rows and columns. SliverGridDelegateWithFixedCrossAxisCount is the most common choice: a fixed number of columns, with row heights determined automatically.
Sizing Columns by Width Instead of Count
For a responsive grid where the column count should adapt to screen width rather than being fixed, use SliverGridDelegateWithMaxCrossAxisExtent instead — you specify a maximum item width, and Flutter fits as many columns as will comfortably fit:
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
),
Choosing Between Them
The decision is almost always about content shape, not performance — both are equally efficient when built with their .builder constructors:
- ListView — content that reads naturally top to bottom: a chat, a feed, settings, search results.
- GridView — content that’s visually browsed rather than read in order: a photo gallery, a product catalog, an icon picker.
For something in between — a list where certain rows should span multiple columns — look at SliverGrid with a custom delegate, or consider whether the layout is really trying to be a Wrap instead.