skip to content

In Flutter's GridView.builder, how do SliverGridDelegateWithFixedCrossAxisCount and SliverGridDelegateWithMaxCrossAxisExtent decide each tile's size?

level: middleimportance: should knowfreq 40%

answer

  1. fixed column count versus max width
  2. column count rounds up
  3. childAspectRatio defaults to 1.0
  4. mainAxisExtent overrides the ratio
  5. tiles get tight constraints

basics

~20 s

FixedCrossAxisCount divides the width into a set number of columns; MaxCrossAxisExtent picks the fewest columns no wider than a maximum. Height is width divided by childAspectRatio, default 1.0, unless mainAxisExtent is set, and each tile is forced to exactly that size.

solid answer

~40 s

`GridView.builder` needs a `gridDelegate` and builds tiles lazily like `ListView.builder`. `SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3)` always makes three columns: tile width is the available width minus spacing, divided by three. `SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: 150)` computes the column count by rounding **up** width / (max + spacing), so on a 500-wide grid it makes four columns of 125. For both, tile height is `width / childAspectRatio`, which defaults to **1.0** — square — unless `mainAxisExtent` sets it directly. `mainAxisSpacing` and `crossAxisSpacing` default to 0. Every tile gets **tight** constraints, so content taller than the computed height overflows; for tiles with an image and text, set `mainAxisExtent` rather than guessing a ratio. The max-extent delegate adapts the column count to tablets without breakpoints.

code

dart · 36 lines
dart
import 'package:flutter/material.dart';

class PartsGrid extends StatelessWidget {
  const PartsGrid({super.key, required this.names, required this.photos});

  final List<String> names;
  final List<ImageProvider> photos;

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      padding: const EdgeInsets.all(8),
      itemCount: names.length,
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 200,
        mainAxisExtent: 240,
        mainAxisSpacing: 8,
        crossAxisSpacing: 8,
      ),
      itemBuilder: (BuildContext context, int index) {
        return Card(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              Expanded(child: Image(image: photos[index], fit: BoxFit.cover)),
              Padding(
                padding: const EdgeInsets.all(8),
                child: Text(names[index], maxLines: 2, overflow: TextOverflow.ellipsis),
              ),
            ],
          ),
        );
      },
    );
  }
}

go deeper

for a junior

Know that GridView.builder needs a gridDelegate and that the two standard delegates fix the column count or the maximum tile width.

for a middle

Compute tile sizes from width, spacing, childAspectRatio and mainAxisExtent, and explain the round-up column count.

for a senior

Diagnose tile overflows from tight constraints, and pick the max-extent delegate for grids that must adapt across phones and tablets.

for a principal

Standardise tile heights and grid delegates in the design system so catalogue screens stay consistent as text size and window width vary.

## GridView.builder `GridView.builder` is the lazy grid: `itemBuilder` runs only for tiles near the viewport, exactly as with `ListView.builder`. It adds one required parameter, `gridDelegate`, which decides the **tile geometry**. The convenience constructors `GridView.count` and `GridView.extent` take a `children` list instead, so they build every tile widget up front — fine for a dozen tiles, not for a 5,000-part catalogue. ## The two standard delegates | | `SliverGridDelegateWithFixedCrossAxisCount` | `SliverGridDelegateWithMaxCrossAxisExtent` | |---|---|---| | You choose | `crossAxisCount` | `maxCrossAxisExtent` | | Column count | fixed | `ceil(width / (maxCrossAxisExtent + crossAxisSpacing))`, at least 1 | | Tile width | (width minus spacing) / count | (width minus spacing) / count, never more than the max | | Adapts to screen width | no | yes | Shared parameters, with defaults: - `mainAxisSpacing: 0.0` — gap between rows; - `crossAxisSpacing: 0.0` — gap between columns; - `childAspectRatio: 1.0` — tile width divided by tile height; - `mainAxisExtent: null` — when set, the tile height in logical pixels, overriding the ratio. ## Worked examples 1. **Fixed count.** Width 800, `crossAxisCount: 4`, `childAspectRatio: 2.0`: tiles are 200 wide and 100 tall. 2. **Max extent.** Width 500, `maxCrossAxisExtent: 150`, no spacing: 500 / 150 is 3.33, rounded up to **4** columns of **125**. Rounding up is what keeps tiles at or below the maximum. 3. **Rotation.** The same max-extent grid at 900 wide gets 6 columns of 150 — no breakpoint code needed. ## Tight tiles and the overflow trap Each tile is laid out with **tight** constraints of exactly the computed width and height. Consequences: - A tile whose `Column` holds a part photo, a name and a price overflows if the computed height is too small — the familiar "bottom overflowed" stripe in debug mode. - Wrapping the tile in `AspectRatio` or `SizedBox` has no effect; the grid has already decided. - Guessing `childAspectRatio` to make a photo-plus-text tile fit breaks when the width or the text size changes. Setting `mainAxisExtent` fixes the height directly, and the content inside can use `Expanded` for the photo. ## Choosing - A fixed number of columns on every device, such as a two-column phone layout: **fixed count**. - Tiles that should stay about the same size while the column count adapts to phones, tablets and desktop windows: **max extent**. - Tiles of different sizes, or a masonry layout: neither — you need a custom `SliverGridDelegate` or a different layout. ## Other details - `GridView.builder` also takes `itemCount`; pass it so the grid has a known end. - The same keep-alive and repaint-boundary defaults as lists apply to tiles. - Horizontal grids swap the axes: the cross axis is vertical and rows play the part of columns.

  • Why do GridView tiles overflow at the bottom after the text size is increased?
    Tile height comes from the delegate — width divided by `childAspectRatio`, or `mainAxisExtent` — and tiles get tight constraints. Larger text needs more height than the fixed tile allows. Give the tile a larger `mainAxisExtent`, let the image take the flexible space with `Expanded`, and cap text lines.
  • How is GridView.count different from GridView.builder with a fixed-count delegate?
    `GridView.count` builds a fixed-count grid from a `children` list, so every tile widget is created up front. `GridView.builder` with `SliverGridDelegateWithFixedCrossAxisCount` lays tiles out the same way but builds them on demand, which suits long catalogues.

saying these in an interview costs you the question

  • childAspectRatio is height divided by width.
  • Grid tiles size themselves to their content by default.
  • MaxCrossAxisExtent rounds the column count down.
  • GridView.count builds its tiles lazily like GridView.builder.
  • Wrapping a grid tile in AspectRatio changes the tile's shape.