什么是弹性布局
在软件开发和 UI 布局中,**弹性布局(Flexible Layout / Flex Layout)**指的是:子元素可以根据可用空间动态伸缩,占用父容器剩余空间或按比例分配空间,而不是固定尺寸。
换句话说,弹性布局的核心就是 空间可以“弹性”分配,不必每个子元素都写死宽高。
在 Flutter 中,弹性布局(Flex) 是一种 按比例分配空间的线性布局,可以理解为 “增强版的 Row/Column”,它允许子 Widget 按比例(flex 值)来分配父容器的剩余空间。
Row 和 Column 本质上就是 Flex 的封装,Flex 的方向不同而已。
Flex 的定义
和前面的Row 和 Column 类似。
Flex({
Key? key,
required Axis direction, // 主轴方向: Axis.horizontal / Axis.vertical
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection? textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline? textBaseline,
List<Widget> children = const <Widget>[],
})
| 属性 | 说明 |
|---|---|
direction |
主轴方向:水平(Row)或垂直(Column) |
mainAxisAlignment |
主轴对齐方式 |
crossAxisAlignment |
交叉轴对齐方式 |
children |
子 Widget 列表 |
mainAxisSize |
主轴占用空间大小(min/max) |
Expanded
Expanded 只能放在 Flex、Column、Row 中使用
Expanded的作用:
- 强制子 Widget 占满剩余空间
flex决定占剩余空间的比例
强制子 Widget 占满剩余空间
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(title: 'Flutter Demo', home: const FlexPage());
}
}
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
// 尽管设置了宽度,但由于使用了 Expanded,仍然会占据剩余空间
Expanded(child: Container(color: Colors.amber, width: 100)),
Container(color: Colors.blue, width: 100, height: 100),
],
),
);
}
}
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
// 尽管设置了宽度,但由于使用了 Expanded,仍然会占据剩余空间
Expanded(child: Container(color: Colors.amber, width: 10)),
Expanded(child: Container(color: Colors.blue, width: 600)),
],
),
);
}
}
被Expanded修饰的两个容器的width被平均分别,尽管两个容器都设置了width的大小
flex 决定占剩余空间的比例
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
// 尽管设置了宽度,但由于使用了 Expanded,仍然会占据剩余空间
Expanded(flex: 2, child: Container(color: Colors.amber, width: 10)),
Expanded(flex: 1, child: Container(color: Colors.blue, width: 600)),
],
),
);
}
}
水平方向总空间被 2:1 比例分配给黄色和蓝色容器
Flexible
子 Widget 可按比例占用空间,但允许自身缩小或包裹内容
fit 可选择 FlexFit.tight(填满)或 FlexFit.loose(包裹内容)
没有Flexible按照设置的width和height布局
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey,
height: 500,
child: Column(
children: [
Container(color: Colors.amber, height: 100),
Container(color: Colors.blue, height: 100),
],
),
),
);
}
}
使用Flexible:
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey,
height: 500,
child: Column(
children: [
Flexible(child: Container(color: Colors.amber)),
Flexible(child: Container(color: Colors.blue)),
],
),
),
);
}
使用Flexible之后,两个容器共同分享了剩余的300的空间,平均每一个是250的高度。
另外,这里我的两个我都没有设置height,这是因为,如果我Flexible 不会自动忽略子组件的固定高度height: 100,而是让子组件 在剩余空间内,按需要伸缩。如果给给 Container 明确写了 height: 100,所以它们不会去填满剩余的 300 高度。所以我在这里去掉了height设置。
fit配置
fit 在 Flutter 的弹性布局里其实就是控制 “给子组件的空间是紧的还是松的”。
FlexFit.loose(默认值):
- 父组件把空间分配好后,你可以决定自己的尺寸(如果设置了固定高度,就用固定高度;没设置就会尽量扩展)
FlexFit.tight:
- 父组件把分配到的剩余空间按比例分好,然后强制子组件填满这块地盘(如果设置了高度和宽度也是无效的)。
Expanded就是Flexible(flex: 1, fit: FlexFit.tight)的简写。
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey,
height: 500,
child: Column(
children: [
Flexible(
fit: FlexFit.tight,
child: Container(color: Colors.amber, height: 100),
),
Flexible(
fit: FlexFit.tight,
child: Container(color: Colors.blue, height: 100),
),
],
),
),
);
}
}
使用fix分配空间
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey,
height: 500,
child: Column(
children: [
// Container(color: Colors.amber),
// Container(color: Colors.blue),
// Container(color: Colors.amber, height: 100),
// Container(color: Colors.blue, height: 100),
// Flexible(child: Container(color: Colors.amber, height: 100)),
// Flexible(child: Container(color: Colors.blue, height: 100)),
Flexible(
flex: 1,
fit: FlexFit.tight,
child: Container(color: Colors.amber, height: 100),
),
Flexible(
flex: 2,
fit: FlexFit.tight,
child: Container(color: Colors.blue, height: 100),
),
],
),
),
);
}
}
500的空间按照1:2的比例分配
Spacer
Spacer 其实就是 一个专门用来“占空”并推开其它组件的工具,它本质上就是 Expanded 的一个简化版。
在 Flutter 的 Row 或 Column 中,Spacer 会:
- 占据可用的剩余空间
- 不能设置宽度/高度
- 可通过
flex参数控制占用比例
内部实现等同于:
Expanded(
flex: x, // 默认 1
child: SizedBox.shrink(), // 一个啥也没有的空盒子
)
Spacer 主要用来在弹性布局中调整组件之间的间距,而不需要手动加 Expanded + Container。
class FlexPage extends StatelessWidget {
const FlexPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.grey,
height: 500,
child: Column(
children: [
Flexible(
flex: 3,
fit: FlexFit.tight,
child: Container(color: Colors.amber, height: 100),
),
const Spacer(), // 默认flex=1
Flexible(
flex: 3,
fit: FlexFit.tight,
child: Container(color: Colors.blue, height: 100),
),
const Spacer(flex: 2), // flex=2
],
),
),
);
}
}