Up to now, some people still find it a bit difficult to use the Flutter Container and Box Model. If by any chance you want to know and understand Flutter Container and Widget, You’re in the right place.
Flutter has borrowed heavily from other technologies including HTML and the Web which have the ideas of borders, padding, and margin. These are collectively called the box model. They’re used to create pleasant-to-the-eyes spacing around and between screen elements. It’s a battle-proven concept that has worked great for the Web so why not borrow it for Flutter?
What is Box Model? The box model simply defines padding, border, and margin.
Let’s say that we have a sized image that we want framed so to speak with a padding of 8, a margin of 10, and a border of 1. Flutter newcomers might try this first:
Image.network( _peopleList[i]['picture']['thumbnail'], padding: 8.0, margin: 10.0, border: 1.0, ),
This would not work since Image widgets don’t have padding, margin, or borders. But you know what does? Containers!. Web developers often apply these things by wrapping elements in a generic container called a <div> and then applying styles to create pleasant spacing for our web pages.
Flutter doesn’t have a <div>, but it does have a div-like widget called a Container which … well … contains other things. In fact, its entire life purpose is to apply layout and styles to the things inside of it. An HTML <div> can hold multiple things, but a Flutter Container only holds one child. It has properties called padding, margin, and decoration. We’ll leave decoration for the styles chapter, but padding and margin are awfully handy for creating nice-looking spacing:
Container( padding: EdgeInsets.all(8.0), margin: EdgeInsets.all(10.0), decoration: BoxDecoration(border: Border.all(width: 1.0)), child: Image.network(thePicture), // Container has *lots* of other properties, many of which // we'll cover in the Styles chapter. ),
Margin and padding might have been easier to learn if they had just allowed us to list four number values
representing the four sides. (They couldn’t make it easy, could they?) Instead, we use a helper widget called
EdgeInsets.
- EdgeInsets.all(8.0) – Same value applied to all four sides evenly.
- EdgeInsets.symmetric(horizontal: 7.0, vertical: 5.0) – Top and bottom are the same. Left and right are the same.
- EdgeInsets.only(top: 20.0, bottom: 40.0, left: 10.0, right: 30.0) – Left, top, right bottom can all be different.
- EdgeInsets.fromLTRB(10.0, 20.0, 30.0, 40.0) – Same as the preceding one but less typing
Also, note that if you want padding only – no other formatting – the Padding widget is a shorthand.
Container( padding: EdgeInsets.all(5), child: Text("Some Cool text"), ),
and
Padding( padding: EdgeInsets.all(5), child: Text("foo"), ),
These two are equivalent, as you would get the same result.
Alignment and positioning within a Container
When you place a small child widget in a large Container, there will be more space in the Container than is needed by its child widget. That child widget will be located in the top-left corner by default. You have the option of positioning it with the alignment property:
Container( width: 150, height: 150, alignment: Alignment(1.0, -1.0), child: Image.network( _peopleList[i]['picture']['thumbnail'], )
Those alignment
numbers represent the horizontal alignment (–1.0 is far left, 0.0 is center, and 1.0 is far right), and the vertical alignment (–1.0 is top, 0.0 is center, and 1.0 is bottom).
But you will probably prefer to use English words rather than numbers when you can:
Container( width: 150, height: 150, alignment: Alignment.centerLeft, child: Image.network( _peopleList[i]['picture']['thumbnail'], ), ),
Alignment can take on any of these values: topLef
t, topCenter
, topRight
, centerLeft
, center
, centerRight
, bottomLeft
, bottomCenter
, and bottomRight
. Now, isn’t that easier to write and easier for your fellow devs to read?
So how do you determine the size of a Container?
You can tell a Container you want it to have a particular width and height, and it will comply when it is able. Width and height both take a simple number that can range from zero to double.infinity
. The value double.infinity
hints to be as large as its parent will
allow.
Now, I know what you’re thinking. “Hey, what do you mean by ‘when it is able’ and ‘hints’ Aren’t there any hard rules? I want Container sizes to be predictable!” And I completely agree. A Container’s size is tough to predict until you know its rules. So, how does it decide then?
Remember two things.
First, a Container is built to contain a child, but having a child is optional. 99% of the time it will have a child. The other 1% of the time we use the Container to provide a background color or to create spacing for its neighbors/siblings.
Second, remember that Flutter determines layout in two phases, down the render tree to
determine Box Constraints and then back up to determine RenderBox (aka “size,” remember?).
We go top down:
- Flutter limits max size by passing Box Constraints down into the Container from its parent.
- The Container is laid back as it tells its parent, “If my neighbors need some space, go ahead and take it. I’ll be as small as you need me to.”
- If height and/or width is set, it honors those up to its max size as determined by its Box Constraints. Note that it is not an error for you to list a size greater than its Box Constraints, it just won’t grow any larger. This is why you can use
double.infinity
without error.
TIP: Setting height and width makes the Container super rigid; it locks in a size. While this is handy when you want to fine-tune your layout, the best practice is to avoid using them unless you have a darn good reason. You generally want to allow widgets to decide their own size.
Then we go bottom up:
- In the 1% of the time that it has no child, it consumes all the remaining space up to its max Box Constraint.
- But most of the time, it has a child so the layout engine looks at the child’s RenderBox.
- If the child’s RenderBox is bigger than my Box Constraints, it clips the child which is a big, fat problem. It’s not technically an error, but it looks bad. So avoid it. When in debug mode, Flutter will draw yellow and black stripes where it has overflowed so the developer doesn’t miss it.
- If the child’s RenderBox is within my Box Constraints, there is leftover room so we look at the alignment property. If
alignment
is not set, we put it in the upper-left corner and make the container tight – it shrinks to fit the child.
Leftover room is just empty. Ifalignment
is set, it makes the container greedy. This sort of makes sense when you think about it because how will it align top/bottom/left/right if it doesn’t add space by growing? - After all this, shrink as needed to honor the margins.
And that’s the basic thing you need to know about the Flutter Container. Have any question? Use the “ASK QUESTION” button to ask your question.