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 属性允许我们美化容器。
注意:一旦使用了 decoration,Container 自身的 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的表现:大小由自身width、height或父组件约束决定。- 实际效果:
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 套 Align | Align 套 Container |
|---|---|---|
| 控制对象 | 控制子组件的对齐方式 | 控制 Container 自身的对齐方式 |
| 容器大小 | 容器大小固定,子组件在里面动 | 容器大小固定,容器在父空间里动 |
| 父级占用 | Container 仅占用设定的 200x200 | Align 默认可填满父级可用空间 |
| 常见用途 | 制作带复杂内部布局的卡片 | 调整组件在页面中的摆放位置 |
4. 特殊情况:Container 的 alignment 属性
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
Row 和 Column 分别用于水平方向与垂直方向排列子组件。
1. 核心属性
| 属性 | 功能 | 备注 |
|---|---|---|
children | 接收 List<Widget> | 放置多个子组件 |
mainAxisAlignment | 主轴对齐方式 | Row 主轴水平,Column 主轴垂直 |
crossAxisAlignment | 交叉轴对齐方式 | Row 交叉轴垂直,Column 交叉轴水平 |
mainAxisSize | 主轴占用大小 | MainAxisSize.max 或 MainAxisSize.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(弹性布局)
Flex 是 Row 与 Column 的底层通用形式。
1. 核心属性
direction:必填。Axis.horizontal等同于Row。Axis.vertical等同于Column。
2. 配合组件:Expanded 与 Flexible
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(流式布局)
当 Row 或 Column 子组件超出可用空间时,容易出现 Overflow。Wrap 可自动换行(或换列)。
1. 核心属性
direction:排列方向,默认Axis.horizontal。spacing:主轴方向子项间距。runSpacing:换行后行与行之间间距。alignment:每一行在主轴上的对齐方式。runAlignment:多行整体在交叉轴上的对齐方式。
2. 知识点
- 组件数量不确定时(如标签云、筛选项),优先考虑
Wrap。 - 相比
Row,Wrap能在小屏设备上更自然适配。
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
- 常见原因:同一层同时设置
decoration和color。 - 解决建议:颜色放到
BoxDecoration(color: ...)中,不再单独写color。
3. Row 子项过多出现黄色黑纹溢出
- 常见原因:
Row不会自动换行。 - 解决建议:改用
Wrap,或配合Expanded/Flexible做弹性压缩。
4. 以为 Container(alignment: ...) 能移动整个容器
- 常见原因:混淆“内容对齐”和“组件定位”。
- 解决建议:
Container.alignment只影响子组件;要移动整个容器,使用外层Align。
十、综合案例:构建一个可自适应的卡片区域
下面的示例同时使用了 Scaffold、setState、Padding、Wrap、Container 和 Align。
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.center、Alignment.bottomRight |
fit | 控制非定位子组件如何适应 Stack 大小 | StackFit.loose(默认)、StackFit.expand |
clipBehavior | 子组件超出范围时的裁剪策略 | Clip.none、Clip.hardEdge |
children | 子组件列表,后面的会覆盖前面的 | [] |
2. 配合组件:Positioned
在 Stack 中,通常使用 Positioned 精确控制子组件位置。
- 常用属性:
top、bottom、left、right、width、height。 - 关键点:一旦子组件被
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. 适用场景建议
- 协议勾选说明(如“同意《用户协议》”)。
- 价格文本(如“原价 + 现价”不同样式)。
- 关键词高亮(搜索结果关键字着色)。