弹性布局允许子组件按照一定比例来分配父容器空间。弹性布局的概念在其它UI系统中也都存在,如H5中的弹性盒子布局,Android中的FlexboxLayout
等。Flutter中的弹性布局主要通过Flex
和Expanded
来配合实现。
Flex
Flex
组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用Row
或Column
会方便一些,因为Row
和Column
都继承自Flex
,参数基本相同,所以能使用Flex的地方基本上都可以使用Row
或Column
。Flex
本身功能是很强大的,它也可以和Expanded
组件配合实现弹性布局。定义如下:
Flex({
Key key,
@required this.direction, //弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
this.mainAxisAlignment = MainAxisAlignment.start,
this.mainAxisSize = MainAxisSize.max,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
this.textBaseline = TextBaseline.alphabetic,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
})
Flex
继承自MultiChildRenderObjectWidget
,对应的RenderObject
为RenderFlex
,RenderFlex
中实现了其布局算法。
Flex(
direction: Axis.horizontal, //horizontal vertical
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('你'),
Text('hello flutter'),
],
),
//等同于
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('你'),
Text('hello flutter'),
],
),
权重组件
- Spacer 是通过 Expanded 实现的,Expanded继承自Flexible。
- 填满剩余空间直接使用Expanded更方便。
- Spacer 用于撑开 Row、Column、Flex 的子组件的空隙。
Flexible
Flexible 组件可以控制 Row、Column、Flex 的子控件占满父组件。定义:
const Flexible({
Key key,
this.flex = 1,
this.fit = FlexFit.loose,
@required Widget child,
}) : super(key: key, child: child);
示例1:Row 中有3个子组件,两边的宽是100,中间的占满剩余的空间
Row(
children: [
Container(width: 100, height: 50, color: Colors.blue),
Flexible(child: Container(height: 50, color: Colors.red)),
Container(width: 100, height: 50, color: Colors.blue),
],
)
示例2:Row中有3个子组件,第一个占1/6,第二个占2/6,第三个占3/6
子组件占比 = 当前子控件 flex / 所有子组件 flex 之和。
Row(
children: [
Flexible(flex: 1, child: Container(height: 50, color: Colors.blue)),
Flexible(flex: 2, child: Container(height: 50, color: Colors.red)),
Flexible(flex: 3, child: Container(height: 50, color: Colors.yellow)),
],
),
Flexible中fit属性 表示填满剩余空间的方式
- tight:必须(强制)填满剩余空间。
- loose:尽可能大的填满剩余空间,但是可以不填满。
loose 详解
下面代码是在“示例1代码”的基础上:
给中间的红色Container添加了Text子控件,此时红色Container就不在充满空间;
再给Container添加对齐方式,此时又填满剩余空间。
是否还记得 Container 组件的大小是如何调整的吗? Container 没设置child时,默认填满父组件; Container 设置了child,默认是适配子控件大小的,即其宽高由子元素决定。 Container 设置了child,又设置了对齐方式,默认填满父组件。
因此是否填满剩余空间取决于子组件是否需要填满父组件。
Column(
children: [
Row(
children: [
Container(width: 100, height: 50, color: Colors.blue),
Flexible(child: Container(height: 50, color: Colors.red, child: Text('Container'))),
Container(width: 100, height: 50, color: Colors.blue),
],
),
SizedBox(height: 10),
Row(
children: [
Container(width: 100, height: 50, color: Colors.blue),
Flexible(
child: Container(
height: 50,
color: Colors.red,
alignment: Alignment.center,
child: Text('Container'),
),
),
Container(width: 100, height: 50, color: Colors.blue),
],
),
],
),
Expanded
Expanded 继承字 Flexible,fit 参数固定为 FlexFit.tight,也就是说 Expanded 必须(强制)填满剩余空间。
可以按比例“扩伸” Row
、Column
和Flex
子组件所占用的空间。
class Expanded extends Flexible {
/// Creates a widget that expands a child of a [Row], [Column], or [Flex]
/// so that the child fills the available space along the flex widget's
/// main axis.
const Expanded({
Key key,
int flex = 1,
@required Widget child,
}) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
}
flex
参数为弹性系数,如果为0或null
,则child
是没有弹性的,即不会被扩伸占用的空间。如果大于0,所有的Expanded
按照其flex的比例来分割主轴的全部空闲空间。
示例:
Container(
padding: EdgeInsets.all(16),
color: Colors.grey,
child: Row(
children: [
// 占空闲空间的 1/3
Expanded(
flex: 1, //主轴为水平反向,设置了flex大于0,则width无效
child: Container(height: 40, color: Colors.red),
),
// 占空闲空间的 2/3
Expanded(
flex: 2,
child: Container(height: 20, color: Colors.green),
),
// 固定空间
Container(width: 50, height: 30, color: Colors.blue),
Text('你好'),
Expanded(
flex: 0,
child: Container(width: 50, height: 30, color: Colors.yellow),
),
],
),
);
Spacer
Spacer 的功能是占用指定比例的空间,其本质也是 Expanded 的实现的,和Expanded的区别是:Expanded 可以设置子控件,而 Spacer 的子控件尺寸是0,因此Spacer适用于撑开 Row、Column、Flex 的子控件的空隙。源码:
class Spacer extends StatelessWidget {
final int flex;
const Spacer({Key key, this.flex = 1})
: assert(flex != null),
assert(flex > 0),
super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
flex: flex,
child: const SizedBox.shrink(),
);
}
}
示例:
Row(
children: [
Container(width: 100, height: 50, color: Colors.blue),
Spacer(flex: 2),
Flexible(flex: 2, child: Container(height: 50, color: Colors.red)),
Spacer(),
Expanded(flex: 1, child: Container(height: 50, color: Colors.pink)),
Container(width: 100, height: 50, color: Colors.blue),
],
),