Understanding Center Widget in Flutter
Suppose that we want to show the logo of our application, at the top and center of the page, like this:
At first, it might seem like an easy task, but there is a lot to learn from it!
Let’s start by implementing the main function, which will instantiate and show the LoginPage widget:
It’d be very likely that we want to add another widget below our logo, like two input boxes for username and password. Therefore, it’s a good idea to start our widget tree with a Column:
So far we’ve got an empty page with “Logo” on top of it, under the status bar:
I can barely see the “Logo” because it’s got hidden under the status bar. Is it possible to tell Flutter that the status bar area is not part of our application? Yes! By wrapping our Scaffold widget in the SafeArea widget:
A widget that insets its child by sufficient padding to avoid intrusions by the operating system. For example, this will indent the child by enough to avoid the status bar at the top of the screen. It will also indent the child by the amount necessary to avoid The Notch on the iPhone X, or other similar creative physical features of the display.
So let’s get it wrapped:
As you can see, now that the area under the status bar is not part of our application, my phone does not make the status bar transparent, because I don’t need to see what’s under it anymore!
Now let’s center the widget.
Question: We know that the children of a Column widget, are by default center aligned horizontally (along the cross axis). So why is not the “Logo” center aligned in the column?
Answer: It is! The point though is that our Column is as wide as its widest child, hence as wide as the “Logo” here.
It’s possible to see the boundaries of our widgets with a tool called “Flutter Inspector”. If you are using Android Studio, select the “Flutter Inspector” tab from the right edge of the IDE and then press the “Toggle Debug Paint” button:
Now the widget boundaries will become visible to you. It can be easily seen that the Column widget is as wide as its widest child, and there is no doubt the Text widget is in the center of the Column horizontally:
There are several options here to center-align the text on the screen. Let us do it this way:
- Wrap the Text widget in another widget that is as wide as the screen,
- Align the Text widget in the center of its parent.
The Center widget is very useful here. Note the following point from the documentation:
A widget that centers its child within itself.
Do not misunderstand it:
- The Center widget is NOT meant to be in the center of its parent
- The child of the Center widget is meant to be placed in the center of the Center widget
Therefore, it would be meaningless for the Center widget to be the same size as its child! The Center widget will always try to expand itself!
Let’s see it in action:
The result with boundaries left visible:
As you can see, the Center widget has become as wide as the screen, and the Text widget is aligned in the center of it.
As I told you, the Center widget will try to be as big as possible. But why it only stretched horizontally? Why didn’t it expand vertically to the bottom of the page? The answer lies in the documentation:
This widget will be as big as possible if its dimensions are constrained and widthFactor and heightFactor are null. If a dimension is unconstrained and the corresponding size factor is null then the widget will match its child’s size in that dimension. If a size factor is non-null then the corresponding dimension of this widget will be the product of the child’s dimension and the size factor. For example if width Factor is 2.0 then the width of this widget will always be twice its child’s width.
- Constraints are given by parents to their children (these are called Incoming Constraints because they come from the parents!)
- Columns are unconstrained or unbounded vertically. This means that Columns tell their children: “You are allowed to take as much as space you can in the vertical direction”.
- Columns are constrained or bounded horizontally. This means that Columns tell their children: “You are not allowed to grow freely in the horizontal direction, there is a certain limit to that!”.
- Most often, when the Incoming Constraint is unbounded in a given direction, the child widget decides to be as small as possible in that direction. i.e. it decides to shrink itself to match the size of its child. Here, the Column tells its children (including the Center widget), “You have no limitation to grow in the vertical direction, you can have any height you want my children!”. Therefore, the Center widget decides to shrink its height to the height of its child (the Text widget). That’s exactly what the documentation of the Center widget says: “If a dimension is unconstrained and the corresponding size factor is null then the widget will match its child’s size in that dimension.”.
- In contrast, when the Incoming Constraint is bounded in a given direction, the child widget will try to be as big as possible in that direction. Here, the Column tells its children (including the Center widget), “You are limited to grow in the horizontal direction, you are only allowed to grow up to the edge of the screen, my children!”. Therefore, the Center widget decides to expand its width as much as the Incoming Constraint allows, up to the edges of the screen. That’s exactly what the documentation of the Center widget says: “This widget will be as big as possible if its dimensions are constrained and widthFactor and heightFactor are null.”.
Now let’s give some margin to the top of our “Logo”. I will do this three ways:
- Wrap the Center widget in a Container, and set the top margin for the Container.
2. Add a “SizedBox” with a specific height before the Center widget:
3. Using the heightFactor of the Center widget! Let’s read the documentation of Center widget again:
If a size factor is non-null then the corresponding dimension of this widget will be the product of the child’s dimension and the size factor. For example if widthFactor is 2.0 then the width of this widget will always be twice its child’s width.
This means that if we set heightFactor to 10, the height of the Center widget will become “10 * height of its child” i.e. “10 * height of Text widget”. This will result in some margin above our “Logo”:
The result of which is:
As you can see, the boundaries of the Center widget show that the height of the Center widget is now ten times the height of the “Logo” Text widget. Of course, this is not a good way of adding a top margin because it will depend on the size of your Text. I used this method only to demonstrate the meaning of the “heightFactor” and “widthFactor” parameter.
The source code is available on github:
Thanks for reading!