Skip to content

Intermediate · Lists, navigation and forms · topic 23 of 50 · 25 min

Grids with GridView

Explanation

GridView shows items in a scrollable grid. GridView.count sets a fixed number of columns for short grids; GridView.builder builds items lazily with a grid delegate: SliverGridDelegateWithFixedCrossAxisCount (fixed columns) or SliverGridDelegateWithMaxCrossAxisExtent (as many columns as fit a maximum tile width). childAspectRatio controls tile shape, and spacing properties add gaps.

When to use a grid

Product catalogues, photo galleries, category tiles and app launchers show items in rows and columns. A grid uses screen width better than a list for visual items like product photos, and users can compare several at once.

A grid has the same two axes as a list. The main axis is the scrolling direction (vertical by default), and the cross axis is across it. "Cross axis count" therefore means the number of columns in a normal vertical grid. mainAxisSpacing is the gap between rows; crossAxisSpacing is the gap between columns.

Like ListView, GridView scrolls by itself. Do not put it directly inside a Column without Expanded, and do not nest it inside another vertical scroll view; make it the main scrolling area of the screen, or use slivers (CustomScrollView with SliverGrid) when one screen needs a header, a grid and a list scrolling together.

GridView.count for small grids

dart
GridView.count(
  crossAxisCount: 3,                 // 3 columns
  mainAxisSpacing: 8,
  crossAxisSpacing: 8,
  padding: const EdgeInsets.all(8),
  children: const [
    Card(child: Center(child: Text('Mobiles'))),
    Card(child: Center(child: Text('Fashion'))),
    Card(child: Center(child: Text('Grocery'))),
    Card(child: Center(child: Text('Home'))),
  ],
)
Like ListView(children: ...), it builds every child at once, so keep it for short grids.

GridView.builder for long grids

dart
GridView.builder(
  padding: const EdgeInsets.all(8),
  itemCount: products.length,
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 200,         // tiles at most 200 wide: 2 columns on phones, more on tablets
    mainAxisSpacing: 8,
    crossAxisSpacing: 8,
    childAspectRatio: 3 / 4,         // width : height
  ),
  itemBuilder: (context, i) => ProductTile(product: products[i]),
)
A grid delegate decides how many columns there are and how big each tile is.
DelegateColumnsGood for
SliverGridDelegateWithFixedCrossAxisCountAlways the number you setDesigns that need exactly 2 or 3 columns
SliverGridDelegateWithMaxCrossAxisExtentAs many as fit the maximum tile widthResponsive grids that adapt to phones, tablets and web

childAspectRatio

childAspectRatio is width divided by height of each tile. 1 makes squares; 3 / 4 makes tiles taller than wide, which suits a product image with a name and price below. If content overflows inside a tile, the tile is too short: lower the ratio (taller tiles) or reduce the content.

For tiles of different heights, like a Pinterest-style photo wall, look at community packages for masonry or staggered grids.

A product tile

dart
class ProductTile extends StatelessWidget {
  const ProductTile({super.key, required this.name, required this.price, required this.imageUrl});
  final String name;
  final int 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, fit: BoxFit.cover, width: double.infinity)),
          Padding(
            padding: const EdgeInsets.all(8),
            child: Text(name, maxLines: 1, overflow: TextOverflow.ellipsis),
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
            child: Text('Rs $price', style: Theme.of(context).textTheme.titleMedium),
          ),
        ],
      ),
    );
  }
}
The image takes the remaining height with Expanded; text is limited to one line.
Think of it like this: A grid is like the shelves of a supermarket: products are arranged in neat rows and columns so shoppers can scan many at once. A delegate is the store manager deciding how many products fit on each shelf depending on the shelf's width.

Common mistakes

  • Fixed 2 columns on tablets: tiles become huge. Use MaxCrossAxisExtent for responsive grids.
  • Overflow inside tiles because the aspect ratio is too wide. Make tiles taller or content smaller.
  • Using GridView.count for hundreds of items. Use GridView.builder.
  • Large network images without caching or resizing: slow scrolling (see module 9).

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Grids with GridView · 1/6

GridView

Grids show items in rows and columns, great for products and photos.

Prefer a video by a teacher? Find free videos on “flutter gridview builder sliver grid delegate childaspectratio product grid” on YouTube ↗

In real life: A category grid for a grocery app

A grocery app's home screen shows categories (Fruits, Vegetables, Dairy, Snacks, Atta and Rice...) as tiles with an icon and label. With SliverGridDelegateWithMaxCrossAxisExtent of 120, phones show three columns and tablets show six, without any extra code.

dart
GridView.builder(
  itemCount: categories.length,
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 120, childAspectRatio: 1),
  itemBuilder: (context, i) => InkWell(
    onTap: () => openCategory(categories[i]),
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [const Icon(Icons.local_grocery_store, size: 40), Text(categories[i])],
    ),
  ),
)
InkWell gives a tap ripple effect.

Practice (0/3 done)

Task 1 · Question

With SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 200), what happens on a wider screen?

Task 2 · Question

Tiles overflow at the bottom. Which childAspectRatio change helps?

Task 3 · Practice

Build a product grid with GridView.builder and a responsive delegate, using a ProductTile card with an image, name (one line) and price. Test it in portrait, landscape and on a tablet emulator (or a resized window on web) and adjust childAspectRatio until nothing overflows.

Quick quiz

Question 1

1.What does childAspectRatio: 1 give?

Question 2

2.Which GridView should you use for 2,000 photos?

Question 3

3.Which delegate always uses exactly 3 columns?

Question 4

4.Which widget adds a tap ripple to a tile?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does childAspectRatio: 1 give?

Answer: Square tiles

Width equals height.

Which GridView should you use for 2,000 photos?

Answer: GridView.builder

Lazy building for long grids.

Which delegate always uses exactly 3 columns?

Answer: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3)

Fixed count fixes the columns.

Which widget adds a tap ripple to a tile?

Answer: InkWell

InkWell shows Material ink feedback.

Which is the right way to build a scrolling screen with a header and a long list?Related

Answer: One LazyColumn with item { header } and items { }

Nested same-direction scrolling crashes.

From Lists and grids

What does grid-template-columns: 2fr 1fr create?Related

Answer: Two columns; the first is twice as wide

Two fr values make two columns sharing space 2:1.

From CSS Grid

What does lazy building mean for ListView.builder?Related

Answer: Items are built only when they are about to be visible

Saves memory and time.

From Lists with ListView

What is the main axis of a Column?Related

Answer: Vertical

Columns lay out vertically.

From Row and Column

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 3 more topics in “Lists, navigation and forms” before its exam: Navigator basics, Routing with go_router, Forms and validation.
  2. Module 6 exam: Lists, navigation and forms · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, State management. 50 topics left in the course before the final exam.

← Previous: Lists with ListView