弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在,如H5中的弹性盒子布局,Android中的FlexboxLayout等。Flutter中的弹性布局主要通过FlexExpanded来配合实现。

Flex

Flex组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用RowColumn会方便一些,因为RowColumn都继承自Flex,参数基本相同,所以能使用Flex的地方基本上都可以使用RowColumnFlex本身功能是很强大的,它也可以和Expanded组件配合实现弹性布局。定义如下:

  1. Flex({
  2. Key key,
  3. @required this.direction, //弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
  4. this.mainAxisAlignment = MainAxisAlignment.start,
  5. this.mainAxisSize = MainAxisSize.max,
  6. this.crossAxisAlignment = CrossAxisAlignment.center,
  7. this.textDirection,
  8. this.verticalDirection = VerticalDirection.down,
  9. this.textBaseline = TextBaseline.alphabetic,
  10. this.clipBehavior = Clip.hardEdge,
  11. List<Widget> children = const <Widget>[],
  12. })

Flex继承自MultiChildRenderObjectWidget,对应的RenderObjectRenderFlexRenderFlex中实现了其布局算法。

  1. Flex(
  2. direction: Axis.horizontal, //horizontal vertical
  3. mainAxisAlignment: MainAxisAlignment.center,
  4. children: [
  5. Text('你'),
  6. Text('hello flutter'),
  7. ],
  8. ),
  9. //等同于
  10. Row(
  11. mainAxisAlignment: MainAxisAlignment.center,
  12. children: [
  13. Text('你'),
  14. Text('hello flutter'),
  15. ],
  16. ),

权重组件

  • Spacer 是通过 Expanded 实现的,Expanded继承自Flexible。
  • 填满剩余空间直接使用Expanded更方便。
  • Spacer 用于撑开 Row、Column、Flex 的子组件的空隙。

Flexible

Flexible 组件可以控制 Row、Column、Flex 的子控件占满父组件。定义:

  1. const Flexible({
  2. Key key,
  3. this.flex = 1,
  4. this.fit = FlexFit.loose,
  5. @required Widget child,
  6. }) : super(key: key, child: child);

示例1:Row 中有3个子组件,两边的宽是100,中间的占满剩余的空间

image.png

  1. Row(
  2. children: [
  3. Container(width: 100, height: 50, color: Colors.blue),
  4. Flexible(child: Container(height: 50, color: Colors.red)),
  5. Container(width: 100, height: 50, color: Colors.blue),
  6. ],
  7. )

示例2:Row中有3个子组件,第一个占1/6,第二个占2/6,第三个占3/6

子组件占比 = 当前子控件 flex / 所有子组件 flex 之和。

image.png

  1. Row(
  2. children: [
  3. Flexible(flex: 1, child: Container(height: 50, color: Colors.blue)),
  4. Flexible(flex: 2, child: Container(height: 50, color: Colors.red)),
  5. Flexible(flex: 3, child: Container(height: 50, color: Colors.yellow)),
  6. ],
  7. ),

Flexible中fit属性 表示填满剩余空间的方式

  • tight:必须(强制)填满剩余空间。
  • loose:尽可能大的填满剩余空间,但是可以不填满。

loose 详解

下面代码是在“示例1代码”的基础上:
给中间的红色Container添加了Text子控件,此时红色Container就不在充满空间;
再给Container添加对齐方式,此时又填满剩余空间。

是否还记得 Container 组件的大小是如何调整的吗? Container 没设置child时,默认填满父组件; Container 设置了child,默认是适配子控件大小的,即其宽高由子元素决定。 Container 设置了child,又设置了对齐方式,默认填满父组件。

因此是否填满剩余空间取决于子组件是否需要填满父组件

image.png

  1. Column(
  2. children: [
  3. Row(
  4. children: [
  5. Container(width: 100, height: 50, color: Colors.blue),
  6. Flexible(child: Container(height: 50, color: Colors.red, child: Text('Container'))),
  7. Container(width: 100, height: 50, color: Colors.blue),
  8. ],
  9. ),
  10. SizedBox(height: 10),
  11. Row(
  12. children: [
  13. Container(width: 100, height: 50, color: Colors.blue),
  14. Flexible(
  15. child: Container(
  16. height: 50,
  17. color: Colors.red,
  18. alignment: Alignment.center,
  19. child: Text('Container'),
  20. ),
  21. ),
  22. Container(width: 100, height: 50, color: Colors.blue),
  23. ],
  24. ),
  25. ],
  26. ),

Expanded

Expanded 继承字 Flexible,fit 参数固定为 FlexFit.tight,也就是说 Expanded 必须(强制)填满剩余空间。
可以按比例“扩伸” RowColumnFlex子组件所占用的空间。

  1. class Expanded extends Flexible {
  2. /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  3. /// so that the child fills the available space along the flex widget's
  4. /// main axis.
  5. const Expanded({
  6. Key key,
  7. int flex = 1,
  8. @required Widget child,
  9. }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
  10. }

flex参数为弹性系数,如果为0或null,则child是没有弹性的,即不会被扩伸占用的空间。如果大于0,所有的Expanded按照其flex的比例来分割主轴的全部空闲空间。

示例:
image.png

  1. Container(
  2. padding: EdgeInsets.all(16),
  3. color: Colors.grey,
  4. child: Row(
  5. children: [
  6. // 占空闲空间的 1/3
  7. Expanded(
  8. flex: 1, //主轴为水平反向,设置了flex大于0,则width无效
  9. child: Container(height: 40, color: Colors.red),
  10. ),
  11. // 占空闲空间的 2/3
  12. Expanded(
  13. flex: 2,
  14. child: Container(height: 20, color: Colors.green),
  15. ),
  16. // 固定空间
  17. Container(width: 50, height: 30, color: Colors.blue),
  18. Text('你好'),
  19. Expanded(
  20. flex: 0,
  21. child: Container(width: 50, height: 30, color: Colors.yellow),
  22. ),
  23. ],
  24. ),
  25. );

Spacer

Spacer 的功能是占用指定比例的空间,其本质也是 Expanded 的实现的,和Expanded的区别是:Expanded 可以设置子控件,而 Spacer 的子控件尺寸是0,因此Spacer适用于撑开 Row、Column、Flex 的子控件的空隙。源码:

  1. class Spacer extends StatelessWidget {
  2. final int flex;
  3. const Spacer({Key key, this.flex = 1})
  4. : assert(flex != null),
  5. assert(flex > 0),
  6. super(key: key);
  7. @override
  8. Widget build(BuildContext context) {
  9. return Expanded(
  10. flex: flex,
  11. child: const SizedBox.shrink(),
  12. );
  13. }
  14. }

示例:
image.png

  1. Row(
  2. children: [
  3. Container(width: 100, height: 50, color: Colors.blue),
  4. Spacer(flex: 2),
  5. Flexible(flex: 2, child: Container(height: 50, color: Colors.red)),
  6. Spacer(),
  7. Expanded(flex: 1, child: Container(height: 50, color: Colors.pink)),
  8. Container(width: 100, height: 50, color: Colors.blue),
  9. ],
  10. ),