📋 学习前提
- 已完成第2课:Dart语言基础
- 理解面向对象编程概念
- 熟悉Flutter开发环境
🎯 课程目标
通过本课程,你将能够:
- 理解Widget在Flutter中的核心地位
- 掌握StatelessWidget和StatefulWidget的区别
- 理解Widget树和Element树的概念
- 掌握Widget的生命周期管理
- 熟练构建自定义Widget
🚀 什么是Widget?
Widget是Flutter应用的基本构建块。在Flutter中,一切都是Widget - 从按钮、文本到布局、动画,甚至整个应用本身都是Widget。
Widget的核心特点
- 不可变(Immutable):Widget一旦创建就不能修改,只能重新创建
- 声明式(Declarative):描述UI应该是什么样子,而不是如何构建
- 组合式(Composition):通过组合简单Widget来构建复杂UI
- 轻量级(Lightweight):创建和销毁成本低,适合频繁重建
💡 重要概念:Widget vs Element vs RenderObject
Flutter框架包含三个核心层次:
- Widget:描述UI的配置(不可变)
- Element:管理Widget的生命周期(可变)
- RenderObject:负责实际的渲染和布局
🌳 Widget树(Widget Tree)
Flutter应用由Widget树构成,每个Widget都可以包含子Widget,形成层次结构:
MaterialApp
├── Scaffold
│ ├── AppBar
│ │ └── Text('我的应用')
│ ├── Body: Column
│ │ ├── Text('欢迎使用Flutter')
│ │ ├── ElevatedButton
│ │ └── Image
│ └── FloatingActionButton
└── ThemeData
Widget树的构建过程
- 构建阶段:Flutter调用Widget的build()方法构建Widget树
- 布局阶段:确定每个Widget在屏幕上的位置和大小
- 绘制阶段:将Widget树绘制到屏幕上
💡 最佳实践:保持Widget树简洁
将复杂的UI拆分成多个小的、可重用的Widget,这样可以提高代码的可读性和维护性。
📊 StatelessWidget(无状态组件)
什么是StatelessWidget?
StatelessWidget是不可变的Widget,一旦创建就不会改变。它适合用于静态内容的展示。
import 'package:flutter/material.dart';
class MyTextWidget extends StatelessWidget {
final String text;
final double fontSize;
// 构造函数
const MyTextWidget({
super.key,
required this.text,
this.fontSize = 16.0,
});
@override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(fontSize: fontSize),
);
}
}
// 使用示例
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyTextWidget(
text: 'Hello, Flutter!',
fontSize: 24.0,
),
),
),
);
}
}
StatelessWidget的特点
- 不可变:属性在创建后不能改变
- 轻量级:创建成本低,适合频繁使用
- 纯函数:build()方法应该是纯函数,不产生副作用
- 性能优化:Flutter可以缓存和重用StatelessWidget
📝 示例:创建可重用的按钮组件
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final Color backgroundColor;
const CustomButton({
super.key,
required this.text,
required this.onPressed,
this.backgroundColor = Colors.blue,
});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: backgroundColor,
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
text,
style: const TextStyle(fontSize: 16),
),
);
}
}
// 使用
CustomButton(
text: '点击我',
onPressed: () {
print('按钮被点击了');
},
backgroundColor: Colors.green,
)
🔄 StatefulWidget(有状态组件)
什么是StatefulWidget?
StatefulWidget是可变的Widget,它包含一个可变的状态对象(State),当状态改变时,Widget会重新构建。
import 'package:flutter/material.dart';
class CounterWidget extends StatefulWidget {
final String title;
const CounterWidget({
super.key,
required this.title,
});
@override
State createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.title,
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20),
Text(
'计数: $_counter',
style: const TextStyle(fontSize: 36),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _decrementCounter,
child: const Text('减少'),
),
const SizedBox(width: 20),
ElevatedButton(
onPressed: _incrementCounter,
child: const Text('增加'),
),
],
),
],
);
}
}
StatefulWidget的生命周期
| 生命周期方法 |
调用时机 |
用途 |
| createState() |
Widget首次插入到树中时 |
创建State对象 |
| initState() |
State对象创建后,build()之前 |
初始化状态,订阅数据流 |
| didChangeDependencies() |
initState()之后,或依赖的InheritedWidget改变时 |
处理依赖关系变化 |
| build() |
需要构建UI时 |
构建Widget树 |
| didUpdateWidget() |
Widget配置改变时 |
响应Widget属性变化 |
| setState() |
需要更新UI时 |
通知框架重新构建 |
| deactivate() |
Widget从树中移除时 |
清理临时状态 |
| dispose() |
Widget永久移除时 |
释放资源,取消订阅 |
💡 重要概念:setState()的作用
setState()方法通知Flutter框架状态已改变,需要重新构建Widget。它会在当前帧结束时触发build()方法。
⚖️ StatelessWidget vs StatefulWidget
| 特性 |
StatelessWidget |
StatefulWidget |
| 可变性 |
不可变 |
可变(通过State对象) |
| 性能 |
更高(可缓存) |
稍低(需要管理状态) |
| 使用场景 |
静态内容展示 |
交互式组件,动态内容 |
| 复杂度 |
简单 |
相对复杂 |
| 生命周期 |
只有build()方法 |
完整的生命周期方法 |
选择原则
- 优先使用StatelessWidget:如果Widget不需要管理内部状态
- 必要时使用StatefulWidget:当Widget需要响应用户交互或数据变化时
- 状态提升:将状态管理提升到更高层级的Widget
💡 最佳实践:状态提升(State Lifting)
将状态管理提升到共同的父Widget中,可以使子Widget保持无状态,提高可重用性。
🔧 Widget的属性和方法
常用Widget属性
// key属性:用于标识Widget
const MyWidget(key: ValueKey('unique_id'));
// 样式属性
Container(
width: 100,
height: 50,
margin: const EdgeInsets.all(10),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.black, width: 1),
),
)
// 子Widget属性
Column(
mainAxisAlignment: MainAxisAlignment.center, // 主轴对齐
crossAxisAlignment: CrossAxisAlignment.start, // 交叉轴对齐
children: [
Text('第一个子元素'),
Text('第二个子元素'),
],
)
Widget的约束系统
Flutter使用约束(Constraints)系统来布局Widget:
- 紧约束(Tight Constraints):强制Widget使用特定大小
- 松约束(Loose Constraints):允许Widget选择自己的大小
- 无界约束(Unbounded Constraints):没有大小限制
📝 示例:理解约束系统
// Container在Column中(松约束)
Column(
children: [
Container(
color: Colors.red,
child: Text('这个Container会尽可能小'),
),
],
)
// Container在Center中(无界约束)
Center(
child: Container(
color: Colors.blue,
width: 100, // 必须指定大小
height: 100,
child: Text('这个Container有固定大小'),
),
)
📚 实践练习
练习1:创建个人信息卡片
创建一个可重用的个人信息卡片组件:
- 使用StatelessWidget创建PersonCard组件
- 包含头像、姓名、年龄、职业等信息
- 支持自定义主题颜色
- 添加点击回调函数
练习2:实现购物车功能
使用StatefulWidget实现简单的购物车:
- 显示商品列表和数量
- 实现增加、减少商品数量的功能
- 计算总价并实时更新
- 添加清空购物车功能
练习3:Widget生命周期实验
创建一个Widget来观察生命周期方法:
- 在每个生命周期方法中添加打印语句
- 观察Widget的创建、更新、销毁过程
- 测试热重载对生命周期的影响
🔍 常见问题解答
Q: 什么时候应该使用StatefulWidget?
A: 当Widget需要管理内部状态(如用户输入、动画、计时器等)时使用StatefulWidget。对于纯展示性内容,使用StatelessWidget。
Q: setState()为什么要在匿名函数中调用?
A: setState()接受一个回调函数,这样可以确保在状态更新完成后才触发重建,避免竞态条件。
Q: Widget的key属性有什么作用?
A: key用于标识Widget,当Widget树重建时,Flutter使用key来匹配新旧Widget,保持状态。
Q: 如何优化Widget的性能?
A: 使用const构造函数创建Widget、避免在build()方法中创建新对象、合理使用Key等。
📖 总结
在本课程中,我们深入学习了Flutter的核心概念 - Widget:
- 理解了Widget在Flutter架构中的核心地位
- 掌握了StatelessWidget和StatefulWidget的区别和用法
- 学习了Widget树和Element树的概念
- 熟悉了StatefulWidget的生命周期管理
- 了解了Widget的约束系统和布局原理
Widget是Flutter开发的基石,熟练掌握Widget的使用是成为Flutter开发者的关键一步。