Containers
Learning objectives
- You know how to use containers.
- You know the basics of how the size of a container is defined.
Previously, we mentioned that Flutter applications are composed of widgets, which are used to define most if not all aspects of an application. These aspects included: (1) the layout of the user interface, (2) the components shown in the interface, (3) the styles used in the interface, and (4) the interactive functionality of the interface.
Widgets that are used to define layout -- areas and behavior of widgets in those areas -- are an essential part of building meaningful user experiences. As an example of such widget, Center is used to center its contents. Here, we look at another widget used for defining areas, the Container.
Container
Container allows defining an area that can be adjusted through a set of properties. The size of a container can be set using width
and height
, and the space around and within the border of the area with margin
and padding
. The color of the area is set using color
. A widget, if any, that the container contains, is set using child
.
The following example demonstrates the use of the container widget. In the example, we create a container with the width set to 200
, the height set to 100
, and the color set to blue (using a constant from the class Colors). The numbers represent pixels.
Setting a child for a container
The child can be any widget. In the following, we expand on the previous example by adding a text widget as a child of the container. By default, the contents of a container are aligned to the left upper corner.
Setting margin and padding
The properties margin
and padding
are used to specify outside and inside borders of the area of a container. The property margin
defines an area outside the container border, while the property padding
defines an area within the container border.
When using margin
and padding
, we define their values with the help of the EdgeInsets class. The named constructor EdgeInsets.all
allows creating offsets to all sides of the a container (i.e. left, right, top, bottom).
The following example demonstrates the above program with margin
set to 30 pixels and padding
set to 10 pixels. In practice, this means that the area outside the container is 30 pixels wide, while the text within the container has a 10 pixel distance from the container border.
The following example shows the program with a red container inside the blue container instead of a text. When you run the program, you'll notice the effect of padding -- the value given to padding
effectively creates the blue border for the red container.
Try changing the above example so that the value given to padding
is 0
. You will notice that the blue color is no longer visible.
Container layout behavior
As outlined in Container documentation, "Container tries, in order: to honor alignment, to size itself to the child, to honor the width, height, and constraints, to expand to fit the parent, to be as small as possible.".
Let's look at some of these in detail.
Honoring alignment
The alignment
property is used to define how a child widget is positioned within the container. The value of alignment
is an instance of an AlignmentGeometry, typically set using constants from the class Alignment.
The following example demonstrates the use of the alignment
property for centering the text widget within the container.
When you run the above program, you notice that the container expands to fill the screen and the text is centered on the screen.
Sizing to a child
If a container has a child widget, the container matches its size to the size of the child widget (given that the size of the container is not defined). In the following example, a blue container has a text widget child. As we observe, the container sizes itself to the child.
However, as we observed before, if the container has an alignment, the container expands to fit the parent and uses the alignment to position the child.
Note that this holds when the parent widget has a defined size (e.g. the screen). We will look into cases where widgets have a so-called unbounded size in subsequent chapters.
Honoring size
If a container is given explicit size using width
and height
, the layout attempts to respect the given size. In the following example, the width of the container is 200 pixels and the height of the container is 100 pixels.
On the other hand, if only width
is defined, the height
of the widget is defined by following the previous rules: (1) honoring alignment and (2) sizing to a child. As an example, the height of the blue container in the following example will be sized to match the text widget within the container.
If we add an alignment -- say Alignment.center
to the above example, we notice that the height of the container will be expanded to match the whole screen.
Expanding to fit the parent
A container tries to expand to fit the size of the parent if it has no alignment, size, or child. In the following example, a container with a width and height contains another container that does not have alignment, size, or a child. When you try out the program, you notice that the blue container is not visible at all.
In practice, the inner container fills the whole area available to it. The available area is inherited from the outer container.
The available space naturally depends on how the parent container is defined, and constraints are respected. For example, if the blue container has a padding, the red container fills all the area available within the constraints of the padding.
Layout in a tree of widgets
The size and layout of a container is defined by both its parents and children.
Size from the child
In the following example, we have three nested containers where the insidemost container has a text widget. The containers do not have an alignment or a size.
Before running the program, guess what the outcome would look like.
As you might have guessed, the sizes of the containers are constrained by the text widget.
In practice, when determining the size of the containers, the layout algorithm traverses the tree of widgets downwards to find a widget with a size. In this case, the text widget has a size. The size of the text widget is propagated upwards in the tree to the green container that holds the text widget. This is used to define the initial size of the green container -- it is sized to a child. Now, as the green container also has a padding, the padding is added to it's size, which is then propagated upwards in the tree to the next container.
The red container is also sized to a child -- that is, the green container. As the red container has a padding, that padding is added to the size received from the green container, which creates the total size of the red container. This is then propagated upwards to the next container -- the blue container.
Honoring parents
In the following example, we have the same three nested containers where the insidemost container has a text widget. In this case, the outermost container has the alignment
property set to Alignment.center
.
Before running the program, guess what the outcome would look like.
As you might have guessed, the blue container expands to fill the screen and has the contents centered.
Again, the layout algorithm traverses the tree of widgets downwards to find a widget with a size; the text widget passes its size to the green widget, which passes the size to the red widget, which passes it upwards. Now, the blue widget has been created so that its' contents are centered, and thus the red widget (and its contents) are centered in the blue widget. As there are no constraints on the size of the blue widget from the children, the blue widget expands to fill the screen.