3354 字
17 分钟
Flutter Day01: 布局与状态管理核心

Flutter 核心知识点笔记#

适用对象:Flutter 初学者到进阶开发者
学习目标:掌握常见布局组件、状态更新机制,并能根据场景选择合适组件

快速导航#

布局遵循规则#

在 Flutter 中,布局遵循“向下传递约束,向上传递尺寸”的原则。

布局核心规则#

  • 父组件向子组件传递约束(Constraints)。
  • 子组件在约束内确定自身尺寸(Size)。
  • 子组件将尺寸回传给父组件,父组件再决定其位置(Position)。

理解这个规则后,很多“为什么不生效”或“为什么溢出”的问题会更容易定位。

一、StatefulWidget 与 setState 状态管理#

在 Flutter 中,当页面上的数据发生变化且需要实时更新 UI 时,通常要使用 StatefulWidget

1. 核心机制#

  • StatefulWidget:组件本身是不可变的,它通过 createState() 创建一个 State 对象。
  • State 对象:存储组件的可变数据(如代码中的 count),并负责构建 UI。
  • setState():通知框架“状态已更改”的关键方法。调用后,Flutter 会将该组件标记为“脏”(dirty),并在下一帧触发 build 方法重新运行,从而根据新数据渲染界面。

2. 代码中的具体应用#

  • 数据定义:int count = 0;(定义在 State 类中)。
  • 状态触发示例:
onPressed: () {
count++; // 1. 修改变量值
setState(() {}); // 2. 触发 UI 刷新
}

注意:setState 内部通常包裹修改逻辑(如 setState(() => count++))。但即使是空回调,只要调用了它,当前 Widget 及其子树就会重新构建。

二、Container 容器组件详解#

Container 是 Flutter 中常用的通用布局组件,它结合了绘制、定位和调整大小等功能。

1. 基础布局属性#

属性描述示例
width / height设置容器的固定宽高(逻辑像素)width: 200
alignment控制子组件(child)在容器内的对齐方式Alignment.center
margin容器外部与其他组件的间距(外边距)EdgeInsets.all(20.0)
padding容器内部边缘与子组件的间距(内边距)EdgeInsets.symmetric(...)
transform对容器进行矩阵变换,如旋转、缩放、平移Matrix4.rotationZ(0.1)

2. BoxDecoration 装饰器#

decoration 属性允许我们美化容器。

注意:一旦使用了 decorationContainer 自身的 color 属性必须移除。

  • color:背景颜色(如 Colors.blue)。
  • borderRadius:设置圆角(BorderRadius.circular(15.0))。
  • border:设置边框颜色和宽度(Border.all(color: Colors.amber, width: 3.0))。

三、Scaffold 页面骨架组件#

Scaffold 是 Material 风格页面的基础骨架组件,通常作为一个页面最外层的结构容器。

它可以快速组织页面常见区域,例如:

  • 顶部导航栏(appBar
  • 页面主体内容区(body
  • 悬浮操作按钮(floatingActionButton
  • 抽屉菜单(drawer
  • 底部导航栏(bottomNavigationBar
  • 底部操作栏(bottomSheet

1. 常用属性#

属性作用常见写法
appBar配置顶部应用栏AppBar(title: Text('首页'))
body页面主体内容Center(child: Text('Hello'))
floatingActionButton悬浮按钮,常用于主要操作FloatingActionButton(...)
drawer左侧抽屉菜单Drawer(child: ListView(...))
bottomNavigationBar底部导航栏BottomNavigationBar(...)
backgroundColor页面背景色Colors.grey[100]

2. 基础示例#

Scaffold(
appBar: AppBar(
title: const Text('Scaffold 示例'),
),
body: const Center(
child: Text('页面内容区域'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 点击后执行主要操作
},
child: const Icon(Icons.add),
),
)

3. 使用建议#

  • 一个页面通常对应一个 Scaffold
  • 如果页面已经有外层 Scaffold,子组件中一般不再重复嵌套,避免布局和返回行为混乱。
  • 需要显示提示条时,可结合 ScaffoldMessenger 使用 SnackBar

四、Align 与 Container 的嵌套关系#

1. Align 放在 Container 内部#

结构:Container -> Align -> Child

  • 谁决定位置:Align 决定子组件(Child)在 Container 内部的位置。
  • Container 的表现:大小由自身 widthheight 或父组件约束决定。
  • 实际效果:Container 像一个固定画框,Align 只是在画框内部移动内容。
  • 注意:不会改变 Container 自身在屏幕中的位置。
Container(
width: 200,
height: 200,
color: Colors.blue,
child: Align(
alignment: Alignment.bottomRight,
child: Text("我在内部"),
),
)

2. Container 放在 Align 内部#

结构:Align -> Container -> Child

  • 谁决定位置:Align 决定整个 Container 在父级空间中的位置。
  • Align 的表现:通常占据父组件可用空间,再按 alignment 摆放子组件。
  • 实际效果:Align 充当“定位员”,可解决 Container 默认靠左上角的问题。
Align(
alignment: Alignment.center,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Text("我在中间"),
),
)

3. 核心区别对比#

维度Container 套 AlignAlign 套 Container
控制对象控制子组件的对齐方式控制 Container 自身的对齐方式
容器大小容器大小固定,子组件在里面动容器大小固定,容器在父空间里动
父级占用Container 仅占用设定的 200x200Align 默认可填满父级可用空间
常见用途制作带复杂内部布局的卡片调整组件在页面中的摆放位置

4. 特殊情况:Containeralignment 属性#

Container 内部已经集成了类似 Align 的逻辑。示例:

Container(
alignment: Alignment.center,
child: Text("Hello"),
)

这在逻辑上等同于“将 Align 放在 Container 内部”。 它只会让文字在容器中居中,不会让容器在页面中居中。

5. 总结建议#

  • 想让“盒子里的内容”换位置:把 Align 放在 Container 里,或直接使用 Container.alignment
  • 想让“整个盒子”换位置:把 Container 放在 Align 里。

五、Padding(内边距组件)#

Padding 专门用于给子组件增加内边距。

1. 核心属性#

  • padding:接收 EdgeInsetsGeometry 类型。
  • 常见写法:
    • EdgeInsets.all(10.0):四周相等。
    • EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0):水平和垂直对称。
    • EdgeInsets.only(left: 10, top: 5):指定单边。
    • EdgeInsets.fromLTRB(left, top, right, bottom):按左上右下设置。

2. 知识点#

  • Padding 是单一职责组件,只负责间距,通常比通用 Container 更轻量。
  • 需要多层间距时,可嵌套多个 Padding

3. 案例代码与实际效果#

Container(
color: Colors.grey.shade300,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),//横向和纵向
child: Container(
color: Colors.blue,
child: const Text(
'有内边距的文本',
style: TextStyle(color: Colors.white),
),
),
),
)

实际效果:蓝色内容块不会贴着灰色边缘,而是与四周保持稳定留白,视觉更舒展。

六、线性布局:Row 与 Column#

RowColumn 分别用于水平方向与垂直方向排列子组件。

1. 核心属性#

属性功能备注
children接收 List<Widget>放置多个子组件
mainAxisAlignment主轴对齐方式Row 主轴水平,Column 主轴垂直
crossAxisAlignment交叉轴对齐方式Row 交叉轴垂直,Column 交叉轴水平
mainAxisSize主轴占用大小MainAxisSize.maxMainAxisSize.min

2. 常见主轴对齐值#

  • MainAxisAlignment.start:起点对齐。
  • MainAxisAlignment.center:居中。
  • MainAxisAlignment.end:终点对齐。
  • MainAxisAlignment.spaceBetween:两端贴边,中间均匀分隔。
  • MainAxisAlignment.spaceAround:每个子项两侧间距相等。
  • MainAxisAlignment.spaceEvenly:所有间距(含两端)完全相等。

3. 案例代码与实际效果#

Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: const [
Icon(Icons.home, size: 32),
Icon(Icons.search, size: 32),
Icon(Icons.person, size: 32),
],
)

实际效果:三个图标在一行中平均分布,左右留白与图标间距保持一致。

七、Flex(弹性布局)#

FlexRowColumn 的底层通用形式。

1. 核心属性#

  • direction:必填。
    • Axis.horizontal 等同于 Row
    • Axis.vertical 等同于 Column

2. 配合组件:ExpandedFlexible#

  • Expanded:强制子项填满主轴剩余空间。
  • Flexible:允许子项参与弹性分配,但不强制填满。
  • flex:分配权重,默认是 1

3. 案例代码与实际效果#

Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 2,
child: Container(height: 60, color: Colors.red),
),
Expanded(
flex: 1,
child: Container(height: 60, color: Colors.green),
),
],
)

实际效果:红色与绿色区域按 2:1 分配宽度,红色区域约为绿色的两倍。

八、Wrap(流式布局)#

RowColumn 子组件超出可用空间时,容易出现 OverflowWrap 可自动换行(或换列)。

1. 核心属性#

  • direction:排列方向,默认 Axis.horizontal
  • spacing:主轴方向子项间距。
  • runSpacing:换行后行与行之间间距。
  • alignment:每一行在主轴上的对齐方式。
  • runAlignment:多行整体在交叉轴上的对齐方式。

2. 知识点#

  • 组件数量不确定时(如标签云、筛选项),优先考虑 Wrap
  • 相比 RowWrap 能在小屏设备上更自然适配。

3. 案例代码与实际效果#

Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final tag in ['Flutter', 'Dart', '状态管理', '布局', '动画', '网络'])
Chip(label: Text(tag)),
],
)

实际效果:标签在一行放不下时会自动换到下一行,避免右侧溢出报错。

九、常见易错点速查#

1. setState 调用了但界面没更新#

  • 常见原因:修改的变量不在当前 State 中,或 UI 没有使用该变量。
  • 解决建议:确认“数据定义位置”和“build 中是否读取该数据”。

2. Container 设置了 decoration 又设置 color#

  • 常见原因:同一层同时设置 decorationcolor
  • 解决建议:颜色放到 BoxDecoration(color: ...) 中,不再单独写 color

3. Row 子项过多出现黄色黑纹溢出#

  • 常见原因:Row 不会自动换行。
  • 解决建议:改用 Wrap,或配合 Expanded/Flexible 做弹性压缩。

4. 以为 Container(alignment: ...) 能移动整个容器#

  • 常见原因:混淆“内容对齐”和“组件定位”。
  • 解决建议:Container.alignment 只影响子组件;要移动整个容器,使用外层 Align

十、综合案例:构建一个可自适应的卡片区域#

下面的示例同时使用了 ScaffoldsetStatePaddingWrapContainerAlign

class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
int count = 0;
final List<String> tags = ['Flutter', '布局', '状态', '组件化', '响应式'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('综合案例')),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => count++),
child: const Icon(Icons.add),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.blue.shade200),
),
child: Align(
alignment: Alignment.centerLeft,
child: Text('点击次数:$count', style: const TextStyle(fontSize: 18)),
),
),
const SizedBox(height: 16),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final tag in tags) Chip(label: Text(tag)),
],
),
],
),
),
);
}
}

实际效果:

  • 点击右下角按钮,顶部计数文本实时刷新。
  • 卡片区域有统一内边距、圆角和边框,视觉层次清晰。
  • 标签区会随屏幕宽度自动换行,小屏设备也不易溢出。

十一、Stack(层叠布局组件)#

Stack 允许子组件按绘制顺序叠放。通常后添加的子组件会显示在更上层。

1. 核心属性#

属性功能常见值
alignment控制未定位子组件的对齐方式Alignment.centerAlignment.bottomRight
fit控制非定位子组件如何适应 Stack 大小StackFit.loose(默认)、StackFit.expand
clipBehavior子组件超出范围时的裁剪策略Clip.noneClip.hardEdge
children子组件列表,后面的会覆盖前面的[]

2. 配合组件:Positioned#

Stack 中,通常使用 Positioned 精确控制子组件位置。

  • 常用属性:topbottomleftrightwidthheight
  • 关键点:一旦子组件被 Positioned 包裹,它会变成“定位组件”,不再受 Stack.alignment 影响。

3. 案例代码与实际效果#

Stack(
children: [
Container(
width: 220,
height: 120,
decoration: BoxDecoration(
color: Colors.blue.shade100,
borderRadius: BorderRadius.circular(12),
),
),
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
child: const Text('NEW', style: TextStyle(color: Colors.white)),
),
),
],
)

实际效果:蓝色卡片右上角叠加一个红色角标,常用于消息数量、活动状态等提示场景。

十二、Text(文本组件)#

Text 用于显示单一样式或轻量样式的字符串内容。

1. 核心属性#

属性作用示例
data文本内容(位置参数)Text('Flutter')
style文本样式TextStyle(fontSize: 16)
textAlign水平对齐方式TextAlign.center
overflow溢出处理TextOverflow.ellipsis
maxLines最大显示行数maxLines: 2
textScaler字体缩放策略(新写法)TextScaler.linear(1.1)

2. TextStyle 常用配置#

  • color:文本颜色。
  • fontSize:字号。
  • fontWeight:字重,如 FontWeight.bold
  • fontStyle:斜体,如 FontStyle.italic
  • letterSpacing:字间距。
  • height:行高倍数,例如 height: 1.5

3. 案例代码与实际效果#

const Text(
'这是一段很长的说明文字,超出两行后会显示省略号,避免破坏列表布局。',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 14, height: 1.4),
)

实际效果:文本最多显示两行,第三行内容被省略,适合卡片列表和新闻摘要场景。

十三、TextSpan 与 RichText(富文本)#

当一句话里需要多种颜色、字号、字重,或希望某一段文字可点击时,应使用 Text.rich / RichText + TextSpan

1. 核心结构#

  • TextSpan 不是组件(Widget),而是文本片段描述对象。
  • 通过 children 形成树形结构,子片段会继承父级样式。
Text.rich(
TextSpan(
text: '同意并继续:',
style: const TextStyle(color: Colors.black87),
children: [
TextSpan(
text: '《用户协议》',
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
const TextSpan(text: ' 与 '),
TextSpan(
text: '《隐私政策》',
style: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
decoration: TextDecoration.underline,
),
),
],
),
)

2. 常用属性速查#

属性说明
text当前片段文本
style当前片段样式
children子片段列表(List<InlineSpan>
recognizer手势识别器,用于点击事件

3. 点击事件注意点#

  • 使用 TapGestureRecognizer 时,若在 StatefulWidget 中长期持有,需要在 dispose 中释放,避免内存泄漏。
  • 对可点击文本建议增加视觉提示(颜色、下划线),提高可用性。

4. 适用场景建议#

  • 协议勾选说明(如“同意《用户协议》”)。
  • 价格文本(如“原价 + 现价”不同样式)。
  • 关键词高亮(搜索结果关键字着色)。
Flutter Day01: 布局与状态管理核心
https://zgq1008.github.io/posts/flutter/flutter_day01/
作者
Telus
发布于
2026-04-06
许可协议
CC BY-NC-SA 4.0
Flutter 系列导航