欢迎来到 Flutter 布局的世界!如果说 Widget 是乐高积木,那么布局 Widget 就是你用来搭建城堡的说明书和骨架。学会了布局,你就能将独立的组件组合成任何你想要的复杂界面。
今天,我们将聚焦于线性布局,即沿着水平或垂直方向排列组件。
学习目标
- 掌握
Container
Widget,学会如何用它来装饰和约束单个子组件。 - 理解
Row
(水平) 和Column
(垂直) 的主轴 (Main Axis) 与交叉轴 (Cross Axis) 的概念。 - 学会使用
mainAxisAlignment
和crossAxisAlignment
来控制子组件的对齐方式。 - 了解
Flex
,以及它与Row
和Column
的关系。
1. Container
:万能的容器
你可以将 Container
想象成网页开发中的 <div>
标签,它是一个功能极其强大的"盒子"。它既可以包裹一个子组件 (child
),也可以通过自身的属性来定义尺寸、边距、背景色、边框等装饰效果。
核心属性
child
: 容器内包裹的子组件。width
,height
: 明确指定容器的宽度和高度。color
: 容器的背景颜色。padding
: 内边距 ,即child
相对于容器边界的距离。margin
: 外边距,即容器相对于其父组件的距离。decoration
(BoxDecoration ): 强大的装饰属性,用于设置边框 (border
)、圆角 (borderRadius
)、阴影 (boxShadow
)、渐变 (gradient
)等。注意:如果同时设置了color
和decoration
,color
属性必须定义在decoration
内部,否则会报错。
代码示例:创建一个带样式的卡片
less
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey, // 设置一个灰色背景,以凸显 margin
appBar: AppBar(title: const Text('Container Demo')),
body: Center(
child: Container(
// 如果设置了 decoration,color 必须放在 decoration 里面
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
color: Colors.blue,
width: 2.0,
),
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
spreadRadius: 3,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
width: 250,
height: 150,
padding: const EdgeInsets.all(20.0), // 内边距
margin: const EdgeInsets.all(30.0), // 外边距
child: const Text(
'这是一个漂亮的 Container!',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
),
),
),
);
}
}
2. Row
& Column
:线性布局的核心
Row
和 Column
是 Flutter 中最常用的布局组件,它们允许你将一组子组件 (children
) 沿水平或垂直方向进行排列。
Row
: 将其children
沿水平方向排列。Column
: 将其children
沿垂直方向排列。
核心概念:主轴 (Main Axis) 与 交叉轴 (Cross Axis)
这是理解 Row
和 Column
的关键!
-
对于
Row
:- 主轴 (Main Axis) 是 水平方向 (从左到右)。
- 交叉轴 (Cross Axis) 是 垂直方向 (从上到下)。
-
对于
Column
:- 主轴 (Main Axis) 是 垂直方向 (从上到下)。
- 交叉轴 (Cross Axis) 是 水平方向 (从左到右)。
核心属性
children
: 一个Widget
列表,是需要被排列的子组件。mainAxisAlignment
: 控制子组件在主轴 上的对齐方式。MainAxisAlignment.start
: 靠前对齐 (默认值)。MainAxisAlignment.end
: 靠后对齐。MainAxisAlignment.center
: 居中对齐。MainAxisAlignment.spaceBetween
: 两端对齐,子组件之间的间隔相等。MainAxisAlignment.spaceAround
: 每个子组件左右两侧的间隔相等,所以首尾两个组件到边界的距离是组件之间间隔的一半。MainAxisAlignment.spaceEvenly
: 所有子组件之间 以及首尾到边界的间隔完全相等。
crossAxisAlignment
: 控制子组件在交叉轴 上的对齐方式。CrossAxisAlignment.start
: 靠前对齐。CrossAxisAlignment.end
: 靠后对齐。CrossAxisAlignment.center
: 居中对齐 (默认值)。CrossAxisAlignment.stretch
: 拉伸子组件,使其填满交叉轴空间。
代码示例:Row
和 Column
的对齐演示
less
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Row & Column Demo')),
body: Column(
children: [
// --- Row 示例 ---
Container(
height: 100, // 给 Row 一个高度以观察 CrossAxisAlignment
color: Colors.yellow,
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
Icon(Icons.star, size: 50),
],
),
),
const Divider(), // 添加一个分割线
// --- Column 示例 ---
Container(
width: 200, // 给 Column 一个宽度以观察 CrossAxisAlignment
color: Colors.blue,
child: const Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch, // 拉伸填满宽度
children: <Widget>[
Text('Item 1', textAlign: TextAlign.center, style: TextStyle(fontSize: 24)),
Text('Item 2', textAlign: TextAlign.center, style: TextStyle(fontSize: 24)),
Text('Item 3', textAlign: TextAlign.center, style: TextStyle(fontSize: 24)),
],
),
),
],
),
),
);
}
}
动手试试 :将上面代码中的 mainAxisAlignment
和 crossAxisAlignment
的值修改为其他枚举值,然后热重载,亲眼看看布局发生了什么变化!这是最好的学习方式。
3. Flex
:Row
和 Column
的"父类"
你可能不知道,Row
和 Column
其实都是 Flex
Widget 的"语法糖"。Flex
是一个更底层的组件,它通过一个 direction
属性来决定其子组件的排列方向。
Row
等价于Flex(direction: Axis.horizontal)
。Column
等价于Flex(direction: Axis.vertical)
。
那我们为什么不直接用 Flex
呢? 因为 Row
和 Column
的代码可读性更高,意图更明确。在绝大多数情况下,你都应该优先使用 Row
和 Column
。只有当你需要动态地 根据某个条件来决定布局是水平还是垂直时,Flex
才会派上用场。
dart
// 这是一个 Row
Row(children: <Widget>[...]);
// 它完全等价于
Flex(
direction: Axis.horizontal,
children: <Widget>[...],
);
总结
今天我们学习了 Flutter 布局的四大金刚:
Container
: 用于包裹、装饰和约束单个 Widget 的瑞士军刀。Row
: 用于将子组件水平排列。Column
: 用于将子组件垂直排列。- 主轴/交叉轴 : 是理解
Row
和Column
对齐方式的钥匙。 Flex
: 是Row
和Column
的底层实现,用于更动态的布局场景。
掌握了这些,你就已经具备了搭建 80% 静态页面的能力。在下一篇教程中,我们将学习如何处理重叠布局 (Stack
) 和弹性布局 (Expanded
),让你的布局能力再上一个台阶!我们下篇见!