层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来(按照代码中声明的顺序)。Flutter中使用StackPositioned这两个组件来配合实现绝对定位。Stack允许子组件堆叠,而Positioned用于根据Stack的四个角来确定子组件的位置。

Stack

https://api.flutter.dev/flutter/widgets/Stack-class.html
叠加布局组件包含 Stack 和 IndexedStack,Stack 组件将子组件叠加显示,根据子组件的顺利依次向上叠加。
定义如下:

  1. Stack({
  2. Key key,
  3. this.alignment = AlignmentDirectional.topStart,
  4. this.textDirection,
  5. this.fit = StackFit.loose,
  6. this.overflow = Overflow.clip,
  7. this.clipBehavior = Clip.hardEdge,
  8. List<Widget> children = const <Widget>[],
  9. })
  • alignment:此参数决定如何去对齐没有定位(没有使用Positioned)或部分定位的子组件。所谓部分定位,在这里特指没有在某一个轴上定位:leftright为横轴,topbottom为纵轴,只要包含某个轴上的一个定位属性就算在该轴上有定位。
  • textDirection:和RowWraptextDirection功能一样,都用于确定alignment对齐的参考系,即:textDirection的值为TextDirection.ltr,则alignmentstart代表左,end代表右,即从左往右的顺序;textDirection的值为TextDirection.rtl,则alignment的start代表右,end代表左,即从右往左的顺序。
  • fit:此参数用于确定没有定位的子组件如何去适应Stack的大小。StackFit.loose表示使用子组件的大小,StackFit.expand表示扩伸到Stack的大小。
  • overflow:此属性决定如何显示超出Stack显示空间的子组件;值为Overflow.clip时,超出部分会被剪裁(隐藏),值为Overflow.visible 时则不会。

Positioned

  1. Positioned({
  2. Key key,
  3. this.left,
  4. this.top,
  5. this.right,
  6. this.bottom,
  7. this.width,
  8. this.height,
  9. @required Widget child,
  10. })

lefttoprightbottom分别代表离Stack左、上、右、底四边的距离。widthheight用于指定需要定位元素的宽度和高度。注意,Positionedwidthheight 和其它地方的意义稍微有点区别,此处用于配合lefttoprightbottom来定位组件,举个例子,在水平方向时,你只能指定leftrightwidth三个属性中的两个,如指定leftwidth后,right会自动算出(left+width),如果同时指定三个属性则会报错,垂直方向同理。

示例

image.png

  1. Container(
  2. color: Colors.grey,
  3. width: 200,
  4. height: 200,
  5. child: Stack(
  6. alignment: AlignmentDirectional.center,
  7. fit: StackFit.loose, //expand 充满; loose 自身大小;passthrough
  8. children: [
  9. // 没有定位,并且alignment值为Alignment.center,所以 垂直水平居中
  10. Container(color: Colors.red, child: Text("Hello")),
  11. // 只定位了水平方向,垂直方向上没有定位,属于部分定位。那么垂直方向上居中,水平方向距左边界 10 像素
  12. Positioned(
  13. left: 10.0,
  14. child: Container(color: Colors.green, child: Text('hello')),
  15. ),
  16. // 水平方向上距右边界 10 像素,垂直方向上距下边界 20 像素
  17. Positioned(
  18. right: 10.0,
  19. bottom: 20.0,
  20. child: Container(color: Colors.yellow, child: Text('hello')),
  21. ),
  22. ],
  23. ),
  24. );

fit属性 示例

image.png

  1. Container(
  2. color: Colors.grey,
  3. width: 200,
  4. height: 200,
  5. child: Stack(
  6. alignment: AlignmentDirectional.center,
  7. fit: StackFit.expand, //expand 充满; loose 自身大小;passthrough
  8. children: [
  9. //由于Stack子元素是堆叠的,所以第一个子组件被第二个遮住了。
  10. Positioned(
  11. left: 10.0,
  12. child: Container(color: Colors.green, child: Text('hello A')),
  13. ),
  14. //由于第二个子组件没有定位,所以fit属性会对它起作用,就会占满Stack
  15. Container(color: Colors.red, child: Text("Hello B")),
  16. //第三个在最上层,所以可以正常显示。
  17. Positioned(
  18. right: 10.0,
  19. bottom: 20.0,
  20. child: Container(color: Colors.yellow, child: Text('hello C')),
  21. ),
  22. ],
  23. ),
  24. );

IndexedStack

这个 Widget 的作用和 Stack 非常类似,唯一的区别是如果你有三个 child Widget,那么它的布局行为是显示你指定的第index个 child Widget ,其他 child Widget 不可见。

image.png

  1. import 'package:flutter/material.dart';
  2. class AVStack extends StatelessWidget {
  3. @override
  4. Widget build(BuildContext context) {
  5. return new MaterialApp(
  6. home: new Scaffold(
  7. appBar: new AppBar(
  8. title: new Text('AVStack'),
  9. ),
  10. body: new Container(
  11. width: 200.0,
  12. height: 200.0,
  13. color: Colors.blue,
  14. child: new IndexedStack(
  15. index: 1,
  16. children: <Widget>[
  17. new Container(
  18. width: 20.0,
  19. height: 20.0,
  20. color: Colors.grey,
  21. ),
  22. new Positioned(
  23. top: 10.0,
  24. right: 10.0,
  25. child: new Container(
  26. width: 50.0,
  27. height: 50.0,
  28. color: Colors.red,
  29. ),
  30. )
  31. ],
  32. ),
  33. )
  34. ),
  35. );
  36. }
  37. }