Flutter Pro Design

Small details that build taste in Flutter.

curated by Kamran BekirovKamran Bekirov

Reserve space for images so layout doesn't jump

An image widget has no size until its bytes arrive, so it takes up no space at first. The moment it loads, it snaps to its real height and pushes everything below it down:

The fix is always the same idea: decide the image's box before the bytes show up:

You know the ratio. Most images go full width and you know their shape (a 16:9 hero, a 4:3 card). Wrap it in AspectRatio and the height is reserved from the first frame:

AspectRatio(
  aspectRatio: 16 / 9,
  child: Image.network(
    url, fit: BoxFit.cover,
  ),
)

You know the exact size. Avatars, thumbnails, list leading icons. Just give it a box:

SizedBox(
  width: 48,
  height: 48,
  child: Image.network(
    url, 
    fit: BoxFit.cover,
  ),
)

A parent already fixes the height. A product card in an e-commerce app with a static height, image on top, text below. Let Expanded hand the image whatever's left:

SizedBox(
  height: 320,
  child: Column(
    children: [
      Expanded(
        child: Image.network(
          url, fit: BoxFit.cover,
        ),
      ),
      // title, price, button
    ],
  ),
)

One catch with Expanded: it needs a bounded main axis. Column has one, but ListView doesn't (its scroll axis is infinite).

Two things people expect to help, but don't:

  • fit does not reserve space. BoxFit.cover, .fitWidth, any of them only decide how the image paints inside a box that's already been sized.
  • Setting only a width still leaves the height to the image's real ratio, unknown until it loads.

Also consider using image_fade for displaying images. It cross-fades images when loaded in instead of popping.