A Flutter guide to visual overlap: padding, viewPadding, and viewInsets

Lê Dân
Flutter Community
Published in
4 min readJul 29, 2021

Flutter is designed to run on a variety of devices. And to run on multiple devices is to adapt to each device’s quirks and features.

One type of device’s quirk that we developers need to pay attention to is the system UIs that overlap our apps. It might be a status bar, a notch, or a bottom indicator.

Fortunately, Flutter supports this out of the box with MediaQuery. You can get the status bar height with MediaQuery.of(context).padding.top, the keyboard height with MediaQuery.of(context).viewInsets.bottom , etc.

However, MediaQuery itself also has some quirks. For example, it has padding but also a viewPadding property. And sometimes they return EdgeInsets.zero even though there are visible obscured system UI.

So in this article, I will attempt to clear things up for those who ran into these little quirks.

For the rest of the article, whenever I refer to padding, viewPadding, or viewInsets, it is the properties of MediaQuery that I’m talking about.

Categorization

First, we should divide the system obscure UIs into 2 categories:

Partially obscured UIs

These are status bars, bottom indicators. We should account for these UIs in our app to create an immersive experience.

Completely obscured UIs

These usually are keyboards. We should adjust our layout to avoid these UIs when they show up.

Notice that when the keyboard appears, the bottom indicator section turns from partially to completely obscured UI.

How Flutter handles this: viewInsets and viewPadding

Flutter’s MediaQuery separates these into 2 properties:

viewInsets → the space needed for completely obscured UIs.

viewPadding → the space needed for partially obscured UIs.

These 2 are independent of each other. When the keyboard appears, viewInsets.bottom changes from 0 to 336, but viewPadding.bottom stays at 34 although we can see that the keyboard consumes the bottom indicator space that our app occupied.

So consider the case above where the completely obscured UI consumed the partially obscured UI, Flutter has a property that accounts for the consumption and it is nonother than padding!

padding → The parts of the display that are still partially obscured by system UI after accounting for the ones that are completely obscured. It is calculated like this:

padding = max(0, viewPadding - viewInsets)

We can see that viewInsets is rather straightforward, but padding and viewPadding can sometimes be used interchangeably, other times it is unwise to do so.

Choose your padding wisely: when to use viewPadding and padding

Layout 1

We decide we want our button to stay behind the keyboard.

  • We need to set our Scaffold.resizeToAvoidBottomInset = false to prevent our Scaffold from resizing.
  • For this case, we should use viewPadding because by then our button is irrelevant to the flow, and using padding might cause our layout to jump down.

Layout 2

We decide we want our button to transform and stay on top when the keyboard appears.

  • We need to check whether the keyboard is visible using viewInsets.bottom > 0
  • For this case, we should use padding because Flutter will account for viewInsets and reduce padding to 0. Our button will stay on top nicely without any further checking.

Why is my viewPadding / padding/ viewInsets is all zeroes?

I previously said that we can check if viewInsets.bottom > 0 to determine if the keyboard is visible. This is only partially true.

Calling MediaQuery.of(context).viewInsets in the wrong place and we will only get EdgeInsets.zero even though the keyboard is clearly showing.

What do I mean by wrong place?

In Flutter, a Widget, usually a Scaffold , can consume viewPadding, padding or viewInsets and modify the MediaQuery which then is passed down to it’s child.

The idea is that if the parent took care of the obscured UIs, then the child shouldn’t worry about them anymore.

If Scaffold has an AppBar, it will consume viewPadding.top.

If Scaffold has a BottomNavigationBar, it will consume viewPadding.bottom .

If Scaffold has resizeToAvoidBottomInset = true, it will consume viewInsets.bottom.

So if you want to check for the keyboard visibility, check them outside of the Scaffold , use an InheritedWidget to pass the data down if needed.

Preparing for Android Q: systemGestureInsets

On a side note, MediaQuery also has a property called systemGestureInsets . This is for Android Q’s gesture navigation. You can find out more about this here.

Wrapping things up

  • viewPadding is for partially obscured UIs
  • viewInsets is for completely obscured UIs
  • padding is derived from viewPadding and viewInsets and should be used only when necessary.
  • Some Widgets can consume these properties and passing down a modified MediaQuery to their child.

https://www.twitter.com/FlutterComm

--

--