In the previous section, we discussed how, through Stack
and Positioned
, we can specify one or more child elements' precise offsets relative to the edges of their parent element, allowing them to overlap. However, if we simply want to adjust a child element's position within its parent, using the Align
component is more straightforward.
28.1 Align
The Align
component can adjust the position of its child, and it is defined as follows:
Align({ Key key, this.alignment = Alignment.center, this.widthFactor, this.heightFactor, Widget child, })
-
alignment
: This requires a value of typeAlignmentGeometry
, which represents the starting position of the child component within its parent.AlignmentGeometry
is an abstract class with two commonly used subclasses:Alignment
andFractionalOffset
, which we will explore in detail below. -
widthFactor
andheightFactor
are properties used to determine the width and height of theAlign
component itself. They are two scaling factors that multiply the width and height of the child element, resulting in the final width and height of theAlign
component. If the values are null, the component will take up as much space as possible.
1. Example
Let’s look at a simple example:
Container( height: 120.0, width: 120.0, color: Colors.blue.shade50, child: Align( alignment: Alignment.topRight, child: FlutterLogo( size: 60, ), ), )
The result is shown in Figure :
FlutterLogo
is a component provided by the Flutter SDK, representing the Flutter logo. In the example above, we explicitly set the Container
's width and height to 120. If we do not explicitly specify the width and height but instead set both widthFactor
and heightFactor
to 2, we can achieve the same effect:
Align( widthFactor: 2, heightFactor: 2, alignment: Alignment.topRight, child: FlutterLogo( size: 60, ), ),
Since the FlutterLogo
has a width and height of 60, the final width and height of Align
will be .
Moreover, we position the FlutterLogo
in the top-right corner of the Container
using Alignment.topRight
. What is Alignment.topRight
? From the source code, we can see its definition:
// Top-right corner static const Alignment topRight = Alignment(1.0, -1.0);
This shows that it is simply an instance of Alignment
. Let's introduce Alignment
.
2. Alignment
Alignment
inherits from AlignmentGeometry
and represents a point within a rectangle. It has two properties, x
and y
, which represent offsets in the horizontal and vertical directions. Alignment
is defined as follows:
Alignment(this.x, this.y)
The Alignment
widget uses the center of the rectangle as the coordinate origin, meaning Alignment(0.0, 0.0)
refers to the center. The values of x
and y
range from -1 to 1, representing the distance from the left to the right and from the top to the bottom of the rectangle, respectively. Thus, two horizontal (or vertical) units equal the width (or height) of the rectangle. For instance, Alignment(-1.0, -1.0)
represents the top-left vertex, while Alignment(1.0, 1.0)
represents the bottom-right vertex. Alignment(1.0, -1.0)
represents the top-right vertex, which is Alignment.topRight
. For convenience, the rectangle's origin, its four vertices, and the endpoints of its four edges are defined as static constants in the Alignment
class.
The actual offset can be calculated using the following formula:
Actual Offset = (Alignment.x * (parentWidth - childWidth) / 2 + (parentWidth - childWidth) / 2, Alignment.y * (parentHeight - childHeight) / 2 + (parentHeight - childHeight) / 2)
where childWidth
is the width of the child element, and childHeight
is the height of the child element.
Now, let’s revisit the previous example. By substituting Alignment(1.0, -1.0)
into the above formula, we can find that the actual offset coordinates for the FlutterLogo
are (60, 0).
Next, let’s see another example:
Align( widthFactor: 2, heightFactor: 2, alignment: Alignment(2, 0.0), child: FlutterLogo( size: 60, ), )
We can first imagine the result. By substituting Alignment(2, 0.0)
into the coordinate transformation formula, we can calculate the actual offset coordinates for the FlutterLogo
as (90, 30). The actual result is shown in Figure :
3. FractionalOffset
FractionalOffset
inherits from Alignment
, and the only difference from Alignment
is the origin of the coordinates! The coordinate origin of FractionalOffset
is the top-left vertex of the rectangle, which is consistent with the layout system, making it easier to understand. The coordinate transformation formula for FractionalOffset
is:
Actual Offset = (FractionalOffset.x * (parentWidth - childWidth), FractionalOffset.y * (parentHeight - childHeight))
Here’s an example:
Container( height: 120.0, width: 120.0, color: Colors.blue[50], child: Align( alignment: FractionalOffset(0.2, 0.6), child: FlutterLogo( size: 60, ), ), )
The actual result is shown in Figure :
By substituting FractionalOffset(0.2, 0.6)
into the coordinate transformation formula, we find that the actual offset for the FlutterLogo
is (12, 36), which matches the actual result.
28.2 Comparison of Align and Stack
It is evident that both Align
and Stack
/Positioned
can be used to specify the offset of child elements relative to their parent element, but they have two main differences:
-
Reference Systems: The reference system for positioning is different;
Stack
/Positioned
can reference the four vertices of the parent container's rectangle, whileAlign
requires thealignment
parameter to determine the coordinate origin. Different alignments correspond to different origins, and the final offset must be calculated using the transformation formula of the alignment. -
Child Elements:
Stack
can have multiple child elements that can stack on top of each other, whileAlign
can only have one child element and does not support stacking.
28.3 Center Component
We have already used the Center
component in previous chapters to center child elements, and now we will formally introduce it. By examining the SDK source code, we see that the Center
component is defined as follows:
class Center extends Align { const Center({ Key? key, double widthFactor, double heightFactor, Widget? child }) : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child); }
The Center
class inherits from Align
and simply lacks an alignment
parameter. Since the alignment
value in the Align
constructor is set to Alignment.center
, we can think of the Center
component as an Align
component with the alignment method already determined (Alignment.center
).
As previously discussed, when widthFactor
or heightFactor
is null, the component's width and height will occupy as much space as possible. This is an important point to note. We will demonstrate it with an example:
... // Omitted unrelated code DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: Center( child: Text("xxx"), ), ), DecoratedBox( decoration: BoxDecoration(color: Colors.red), child: Center( widthFactor: 1, heightFactor: 1, child: Text("xxx"), ), )
The result is shown in Figure :
28.4 Summary
This section focused on the Align
component and the two offset classes, Alignment
and FractionalOffset
. Readers should understand the differences between these two offset classes and their respective coordinate transformation formulas. Additionally, it is recommended that readers prioritize using FractionalOffset
when precise offsets are needed, as its coordinate origin aligns with the layout system, making it easier to calculate the actual offset.
We also introduced the relationship between the Align
component, Stack
/Positioned
, and Center
, allowing readers to compare and understand them.
Furthermore, readers familiar with web development may notice that the features of the Align
component are very similar to relative positioning (position: relative
) in web development. Indeed! In most cases, we can use the Align
component to achieve the same effect as relative positioning in web development, which readers can remember for comparison.