Flutter (27): Stack, Positioned

Time: Column:Mobile & Frontend views:215

Layered layout is similar to absolute positioning in the Web or Frame layout in Android, where child components can position themselves based on their distance from the four corners of the parent container. Layered layout allows child components to stack in the order they are declared in the code. In Flutter, absolute positioning is achieved using the Stack and Positioned components. Stack allows child components to be stacked, while Positioned is used to position child components relative to the four corners of the Stack.

27.1 Stack

The Stack component is defined as follows:

Stack({
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.clipBehavior = Clip.hardEdge,
  List<Widget> children = const <Widget>[],
})
  • alignment: This parameter determines how to align child components that are not positioned (i.e., not using Positioned) or are only partially positioned. Partial positioning here refers to when a child is positioned along only one axis. For example, if a child is positioned using left or right, it's positioned along the horizontal axis; top or bottom positions it along the vertical axis. If any positioning property is specified on one axis, the child is considered positioned along that axis.

  • textDirection: Like in Row and Wrap, this determines the reference system for alignment. For example, if textDirection is set to TextDirection.ltr, alignment.start refers to the left and alignment.end refers to the right, which is the left-to-right order. If set to TextDirection.rtl, alignment.start refers to the right and alignment.end refers to the left, which is right-to-left order.

  • fit: This parameter determines how unpositioned children should fit within the Stack. StackFit.loose means the child will take up its own size, while StackFit.expand means it will expand to fit the size of the Stack.

  • clipBehavior: This property determines how to clip the parts of the children that extend outside the Stack. The Clip enum defines various clipping methods. Clip.hardEdge means direct clipping without applying anti-aliasing. More information can be found in the source code comments.

27.2 Positioned

The default constructor for Positioned is as follows:

const Positioned({
  Key? key,
  this.left, 
  this.top,
  this.right,
  this.bottom,
  this.width,
  this.height,
  required Widget child,
})
  • left, top, right, and bottom represent the distance from the respective side of the Stack.

  • width and height are used to specify the width and height of the positioned element. Note that the meaning of width and height in Positioned is slightly different from their use elsewhere. Here, they are used in combination with left, top, right, and bottom to position the component. For example, on the horizontal axis, you can only specify two out of the three properties (left, right, width). If left and width are specified, right will automatically be calculated as (left + width). Specifying all three will cause an error. The same principle applies to the vertical axis.

27.3 Example

In the following example, we demonstrate the features of Stack and Positioned by positioning several Text components:

// Use ConstrainedBox to ensure the Stack occupies the full screen
ConstrainedBox(
  constraints: BoxConstraints.expand(),
  child: Stack(
    alignment: Alignment.center,  // Specify alignment for unpositioned or partially positioned widgets
    children: <Widget>[
      Container(
        child: Text("Hello world", style: TextStyle(color: Colors.white)),
        color: Colors.red,
      ),
      Positioned(
        left: 18.0,
        child: Text("I am Jack"),
      ),
      Positioned(
        top: 18.0,
        child: Text("Your friend"),
      ),
    ],
  ),
);

The result is shown in Figure :

Flutter (27): Stack, Positioned

In this example:

  • The first Text component ("Hello world") does not specify any positioning, and the alignment value is set to Alignment.center, so it is centered within the Stack.

  • The second Text component ("I am Jack") specifies only horizontal positioning (left), making it partially positioned. Vertically, it follows the alignment setting, meaning it is centered along the vertical axis.

  • The third Text component ("Your friend") follows a similar principle but is centered horizontally as it only specifies vertical positioning (top).

Now, let’s add a fit property to the Stack and rearrange the child components:

Stack(
  alignment: Alignment.center,
  fit: StackFit.expand,  // Unpositioned widgets will take up the entire Stack space
  children: <Widget>[
    Positioned(
      left: 18.0,
      child: Text("I am Jack"),
    ),
    Container(
      child: Text("Hello world", style: TextStyle(color: Colors.white)),
      color: Colors.red,
    ),
    Positioned(
      top: 18.0,
      child: Text("Your friend"),
    ),
  ],
);

The result is shown in Figure :

Flutter (27): Stack, Positioned

As you can see, because the second Text component is unpositioned, the fit property causes it to take up the entire space of the Stack. Since Stack children are stacked on top of each other, the first Text component is covered by the second, and the third is displayed on top.