Flutter Lesson 20 of 83 3 min read
GridView and Wrap in Flutter
Learn GridView in Flutter: fixed column counts, responsive grids with max extent, GridView.builder, aspect ratios, and Wrap for chips.
On this page
GridView arranges items in rows and columns and scrolls. Photo galleries, product catalogues and dashboards use it.
A fixed number of columns #
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Gallery')),
body: GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
),
itemCount: 30,
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
color: Colors.primaries[index % Colors.primaries.length].shade200,
borderRadius: BorderRadius.circular(12),
),
alignment: Alignment.center,
child: Text('$index'),
);
},
),
),
),
);
}
Like ListView.builder, this builds only the visible cells.
| Delegate property | Meaning |
|---|---|
crossAxisCount | Number of columns |
mainAxisSpacing | Gap between rows |
crossAxisSpacing | Gap between columns |
childAspectRatio | Width divided by height of each cell. Default 1, a square |
The size of a cell #
A grid gives every cell the same size and ignores the size its child asks for. You control the shape with childAspectRatio.
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 4, // taller than wide: good for product cards
)
If the content of your cells overflows, the cells are too short. Lower the ratio, or set an exact height with mainAxisExtent: 220.
A grid that adapts to the screen #
Fixed column counts look wrong on tablets. Give a maximum cell width and let Flutter work out how many columns fit.
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200, // no cell wider than 200
mainAxisSpacing: 12,
crossAxisSpacing: 12,
childAspectRatio: 0.75,
),
itemCount: products.length,
itemBuilder: (context, i) => ProductCard(product: products[i]),
)
A phone shows two columns, a tablet four or five, a desktop more, with no extra code.
Shortcuts for small grids #
GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16),
mainAxisSpacing: 16,
crossAxisSpacing: 16,
children: const [
Card(child: Center(child: Text('Orders'))),
Card(child: Center(child: Text('Customers'))),
Card(child: Center(child: Text('Products'))),
Card(child: Center(child: Text('Reports'))),
],
)
GridView.extent(maxCrossAxisExtent: 200, children: [...]) is the adaptive equivalent. Both build all children at once, so keep them for small, fixed sets.
A product card for a grid #
class ProductCard extends StatelessWidget {
const ProductCard({super.key, required this.name, required this.price, required this.imageUrl});
final String name;
final String price;
final String imageUrl;
@override
Widget build(BuildContext context) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Image.network(imageUrl, width: double.infinity, fit: BoxFit.cover),
),
Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, maxLines: 1, overflow: TextOverflow.ellipsis),
const SizedBox(height: 4),
Text(price, style: const TextStyle(fontWeight: FontWeight.bold)),
],
),
),
],
),
);
}
}
The image is in an Expanded, so it absorbs whatever height is left after the text.
Wrap: items that flow onto the next line #
When items have different widths, such as tags or filter chips, a grid is the wrong tool. Wrap lays children out in a line and starts a new line when it runs out of room.
Wrap(
spacing: 8, // gap between items
runSpacing: 8, // gap between lines
children: [
for (final tag in ['Dart', 'Flutter', 'Firebase', 'State management', 'UI'])
Chip(label: Text(tag)),
],
)
Wrap does not scroll and builds all its children, so use it for a modest number of items.
Grids of items with different heights #
Standard grids need equal cells. For a Pinterest-style layout, use the flutter_staggered_grid_view package.
Try it yourself #
Build a category screen: a grid with a maximum cell width of 180, where each cell is a card with a large icon and a label. Above it, add a Wrap of filter chips.