Skip to content

Staggered

Romain Rastel edited this page Dec 28, 2021 · 4 revisions

Staggered Layout

The StaggeredGrid let's you creating a custom layout made of tiles with different sizes.

This layout is only intended for a small number of items.

StaggeredGrid.count(
  crossAxisCount: 4,
  mainAxisSpacing: 4,
  crossAxisSpacing: 4,
  children: const [
    StaggeredGridTile.count(
      crossAxisCellCount: 2,
      mainAxisCellCount: 2,
      child: Tile(index: 0),
    ),
    StaggeredGridTile.count(
      crossAxisCellCount: 2,
      mainAxisCellCount: 1,
      child: Tile(index: 1),
    ),
    StaggeredGridTile.count(
      crossAxisCellCount: 1,
      mainAxisCellCount: 1,
      child: Tile(index: 2),
    ),
    StaggeredGridTile.count(
      crossAxisCellCount: 1,
      mainAxisCellCount: 1,
      child: Tile(index: 3),
    ),
    StaggeredGridTile.count(
      crossAxisCellCount: 4,
      mainAxisCellCount: 2,
      child: Tile(index: 4),
    ),
  ],
),

Constructors

This StaggeredGridTile has two main named constructors.

The other constructor can be used if you want a more specific behavior.

The count constructor

This constructor is used to divide the cross axis by a specific number, whatever the available size is. You can control this through the crossAxisCount parameter.

Use this constructor when you want to always have the same number of division in the cross axis extent.

The extent constructor

This constructor is used to set a maximum extent for each division in the cross axis. You can control this through the maxCrossAxisExtent parameter.

For example, in the case of a vertical 408px wide grid and without spacings between tiles, if you set the maxCrossAxisExtent to 204, the grid will have two 204px wide columns. If you set the maxCrossAxisExtent to 150, it will have three 136px wide columns (because the layout will try to give has mush space to each column without exceeding 150px).

Use this constructor when you want to show more or less items in the cross axis extent depending on the available space.

Spacings

All constructors have a mainAxisSpacing and a crossAxisSpacing parameters.

The mainAxisSpacing define the amount of pixels between two tiles in the main axis.

The crossAxisSpacing define the amount of pixels between two tiles in the cross axis.

For example, in a vertical grid, the columns are separated by a space with a value of crossAxisSpacing and each tile is separated from the next one within the same column by a space with a value of mainAxisSpacing.

AxisDirection

The axisDirection parameter indicates in which direction new items are added. If its value is null, it takes the axis direction of the ambient Scrollable or it is set to AxisDirection.down if there are no ambient Scrollable.

StaggeredGridTile

Each child of the StaggeredGrid can have a StaggeredGridTile parent widget to specify the size of the tile. If a child has not a StaggeredGridTile parent, it will be a square fitting in one column only.

The StaggeredGridTile comes with two constructors. Each of them is independent of the constructor used with StaggeredGrid (although they have the same name).

The count constructor

This constructor is used to indicate the number of cells, occupied by a tile, in the main axis.

For example, in a vertical grid, the configuration below will create a tile occupying 2 columns and having a height of one column wide:

StaggeredGridTile.count(
  crossAxisCellCount: 2,
  mainAxisCellCount: 1,
  child: Tile(index: 1),
),

The mainAxisCellCount is not needed to be an integer, you can set a any number greater than 0:

StaggeredGridTile.count(
  crossAxisCellCount: 2,
  mainAxisCellCount: 0.5,
  child: Tile(index: 1),
),

The extent constructor

This constructor is used to explicitly indicate the number of pixels occupied by the tile in the main axis.

For example, in a vertical grid, the configuration below will create a tile occupying 2 columns and having a height of 200 logical pixels:

StaggeredGridTile.extent(
  crossAxisCellCount: 2,
  mainAxisExtent: 200,
  child: Tile(index: 1),
),

TextDirection

This layout uses the ambient TextDirection to know if it should create the grid from left-to-right or right-to-left.

Just add a Directionality widget above the StaggeredGrid to set the order in which the tiles are rendered.

Directionality(
  textDirection: TextDirection.rtl,
  child: StaggeredGrid.count(
    crossAxisCount: 4,
    mainAxisSpacing: 4,
    crossAxisSpacing: 4,
    children: const [
      StaggeredGridTile.count(
        crossAxisCellCount: 2,
        mainAxisCellCount: 2,
        child: Tile(index: 0),
      ),
      StaggeredGridTile.count(
        crossAxisCellCount: 2,
        mainAxisCellCount: 1,
        child: Tile(index: 1),
      ),
      StaggeredGridTile.count(
        crossAxisCellCount: 1,
        mainAxisCellCount: 1,
        child: Tile(index: 2),
      ),
      StaggeredGridTile.count(
        crossAxisCellCount: 1,
        mainAxisCellCount: 1,
        child: Tile(index: 3),
      ),
      StaggeredGridTile.count(
        crossAxisCellCount: 4,
        mainAxisCellCount: 2,
        child: Tile(index: 4),
      ),
    ],
  ),
),

FAQ

Why this component is not built to handle a lot of children?

The StaggeredGrid is not a GridView and does not have a Sliver alter-ego. It's like a Column. All children are layout even if they are not visible.

They are two main reasons for this:

  1. The layout mechanism doesn't work very well with infinite scroll.

For example, in the layout below, the light blue area is what it's currently visible for the user. The children 1, 2, 3 and 4 are visible.

If we add a 1x1 tile, this is how the layout will place the next child:

As you can see, it will not be visible for the user. Having various sizes in the cross axis in not great for this kind of use case.

  1. I didn't find a performant algorithm to do it.

The initial version of this package had performance issues and finding an efficient algorithm for this use case is not in my priorities for the moment.