Skip to content

Masonry

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

Masonry Layout

The Masonry layout is one of the four types of Image lists, showcased in Material Design.

Like the built-in grid component, there is a MasonryGridView (the equivalent of the GridView widget):

MasonryGridView.count(
  crossAxisCount: 4,
  mainAxisSpacing: 4,
  crossAxisSpacing: 4,
  itemBuilder: (context, index) {
    return Tile(
      index: index,
      extent: (index % 5 + 1) * 100,
    );
  },
),

There is also a sliver called SliverMasonryGrid (the equivalent of the SliverGrid):

CustomScrollView(
  slivers: [
    SliverMasonryGrid.count(
      crossAxisCount: 4,
      mainAxisSpacing: 4,
      crossAxisSpacing: 4,
      itemBuilder: (context, index) {
        return Tile(
          index: index,
          extent: (index % 5 + 1) * 100,
        );
      },
    ),
  ],
),

Constructors

This SliverMasonryGrid has two main named constructors which you can also use with the MasonryGridView. The other constructors 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.

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 MasonryGridView (or CustomScrollView) to set the order in which the tiles are rendered.

Directionality(
  textDirection: TextDirection.rtl,
  child: MasonryGridView.count(
    crossAxisCount: 4,
    mainAxisSpacing: 4,
    crossAxisSpacing: 4,
    itemBuilder: (context, index) {
      return Tile(
        index: index,
        extent: (index % 5 + 1) * 100,
      );
    },
  ),
),

FAQ

Why this is not implemented as GridDelegate like the Quilted or Woven layouts?

Each time the grid is scrolled, the GridDelegate is called to compute the index of the visible tiles. It's quite performant for a regular grid to compute them for any scroll offset because the size of each tile is fixed.

The masonry layout, let's you provide tiles with dynamic main axis extents. In order to compute the index of the visible tiles, we would have to compute all the sizes of the tiles from the beginning, and this for each scroll interaction. This is not possible to have a performant way of doing this with the GridDelegate. That's why this layout is not implemented as a GridDelegate and has its own MasonryGridView widget.

Examples