Flutter StatefulWidget 完全指南:让你的应用“动“起来
Flutter中的StatefulWidget(有状态组件)是实现动态界面的核心。与StatelessWidget(无状态组件)不同,StatefulWidget可以响应数据变化和用户交互,通过setState()方法触发界面更新。典型应用场景包括计数器、开关切换和输入框实时显示等。StatefulWidget由两部分组成:不可变的Widget配置和可变的State状态管理。关键点在于必须通过se
·
什么是有状态组件(StatefulWidget)?
在 Flutter 中,组件分为两种:
- StatelessWidget(无状态组件) - 静态的,一旦创建就不会改变
- StatefulWidget(有状态组件) - 动态的,可以根据用户操作或数据变化而更新
简单理解:
- 无状态组件 = 一张静态的照片
- 有状态组件 = 一段可以播放的视频
为什么需要有状态组件?
想象这些场景:
- 点击按钮,计数器数字增加 ✅
- 输入框输入文字,界面显示输入内容 ✅
- 切换开关,按钮颜色改变 ✅
- 下拉刷新,列表数据更新 ✅
这些都需要界面"动态更新",就必须用 StatefulWidget。
无状态 vs 有状态
无状态组件示例
class MyText extends StatelessWidget {
Widget build(BuildContext context) {
return Text('我是静态文本');
}
}
这个文本永远显示"我是静态文本",不会改变。
有状态组件示例
class Counter extends StatefulWidget {
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0; // 这是状态
Widget build(BuildContext context) {
return Column(
children: [
Text('点击了 $count 次'),
ElevatedButton(
onPressed: () {
setState(() {
count++; // 改变状态,界面会自动更新
});
},
child: Text('点我'),
),
],
);
}
}
点击按钮,数字会增加,界面会更新!
StatefulWidget 的结构
有状态组件由两部分组成:
// 第一部分:StatefulWidget(组件本身)
class MyWidget extends StatefulWidget {
_MyWidgetState createState() => _MyWidgetState();
}
// 第二部分:State(状态管理)
class _MyWidgetState extends State<MyWidget> {
// 这里定义状态变量
Widget build(BuildContext context) {
// 这里构建界面
return Container();
}
}
为什么要分两部分?
StatefulWidget- 不可变的配置信息State- 可变的状态数据
这样设计可以让 Flutter 高效地重建界面。
核心概念:setState()
setState() 是有状态组件的灵魂,它告诉 Flutter:“我的数据变了,请重新绘制界面!”
正确用法
// ✅ 正确:在 setState 中修改状态
setState(() {
count++;
});
错误用法
// ❌ 错误:直接修改状态,界面不会更新
count++;
记住:想让界面更新,必须用 setState()!
基础示例
示例1:简单计数器
import 'package:flutter/material.dart';
class CounterApp extends StatefulWidget {
_CounterAppState createState() => _CounterAppState();
}
class _CounterAppState extends State<CounterApp> {
int _counter = 0;
void _increment() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('计数器')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'你点击了',
style: TextStyle(fontSize: 20),
),
Text(
'$_counter',
style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold),
),
Text(
'次',
style: TextStyle(fontSize: 20),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _increment,
child: Icon(Icons.add),
),
);
}
}
示例2:开关切换
class ToggleSwitch extends StatefulWidget {
_ToggleSwitchState createState() => _ToggleSwitchState();
}
class _ToggleSwitchState extends State<ToggleSwitch> {
bool _isOn = false;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('开关')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: _isOn ? Colors.green : Colors.grey,
shape: BoxShape.circle,
),
child: Center(
child: Text(
_isOn ? '开' : '关',
style: TextStyle(fontSize: 40, color: Colors.white),
),
),
),
SizedBox(height: 30),
Switch(
value: _isOn,
onChanged: (value) {
setState(() {
_isOn = value;
});
},
),
],
),
),
);
}
}
示例3:文本输入实时显示
class TextInputDemo extends StatefulWidget {
_TextInputDemoState createState() => _TextInputDemoState();
}
class _TextInputDemoState extends State<TextInputDemo> {
String _inputText = '';
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('输入演示')),
body: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
TextField(
onChanged: (value) {
setState(() {
_inputText = value;
});
},
decoration: InputDecoration(
labelText: '输入点什么',
border: OutlineInputBorder(),
),
),
SizedBox(height: 20),
Text(
'你输入了:$_inputText',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 10),
Text(
'字数:${_inputText.length}',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
],
),
),
);
}
}
生命周期
StatefulWidget 有完整的生命周期,就像人的一生:出生 → 成长 → 死亡。
生命周期方法
class LifecycleDemo extends StatefulWidget {
_LifecycleDemoState createState() {
print('1. createState - 创建状态对象');
return _LifecycleDemoState();
}
}
class _LifecycleDemoState extends State<LifecycleDemo> {
void initState() {
super.initState();
print('2. initState - 初始化,只执行一次');
// 在这里做初始化工作:加载数据、订阅事件等
}
void didChangeDependencies() {
super.didChangeDependencies();
print('3. didChangeDependencies - 依赖改变时调用');
}
Widget build(BuildContext context) {
print('4. build - 构建界面(会多次调用)');
return Container(
child: Text('生命周期演示'),
);
}
void didUpdateWidget(LifecycleDemo oldWidget) {
super.didUpdateWidget(oldWidget);
print('5. didUpdateWidget - 组件更新时调用');
}
void deactivate() {
print('6. deactivate - 组件被移除时调用');
super.deactivate();
}
void dispose() {
print('7. dispose - 组件销毁时调用');
// 在这里做清理工作:取消订阅、释放资源等
super.dispose();
}
}
生命周期执行顺序
创建阶段:
createState → initState → didChangeDependencies → build
更新阶段:
didUpdateWidget → build
销毁阶段:
deactivate → dispose
常用生命周期方法
| 方法 | 用途 | 调用次数 |
|---|---|---|
initState() |
初始化数据、订阅事件 | 1次 |
build() |
构建界面 | 多次 |
dispose() |
清理资源、取消订阅 | 1次 |
实战案例
案例1:待办事项列表
class TodoList extends StatefulWidget {
_TodoListState createState() => _TodoListState();
}
class _TodoListState extends State<TodoList> {
List<String> _todos = [];
TextEditingController _controller = TextEditingController();
void _addTodo() {
if (_controller.text.isNotEmpty) {
setState(() {
_todos.add(_controller.text);
_controller.clear();
});
}
}
void _removeTodo(int index) {
setState(() {
_todos.removeAt(index);
});
}
void dispose() {
_controller.dispose(); // 清理控制器
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('待办事项')),
body: Column(
children: [
Padding(
padding: EdgeInsets.all(10),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: '输入待办事项',
border: OutlineInputBorder(),
),
),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: _addTodo,
child: Text('添加'),
),
],
),
),
Expanded(
child: ListView.builder(
itemCount: _todos.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(_todos[index]),
trailing: IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () => _removeTodo(index),
),
);
},
),
),
],
),
);
}
}
案例2:颜色选择器
class ColorPicker extends StatefulWidget {
_ColorPickerState createState() => _ColorPickerState();
}
class _ColorPickerState extends State<ColorPicker> {
Color _selectedColor = Colors.blue;
final List<Color> _colors = [
Colors.red,
Colors.blue,
Colors.green,
Colors.orange,
Colors.purple,
Colors.pink,
];
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('颜色选择器')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: _selectedColor,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: _selectedColor.withOpacity(0.5),
blurRadius: 20,
spreadRadius: 5,
),
],
),
),
SizedBox(height: 30),
Text(
'选择一个颜色',
style: TextStyle(fontSize: 20),
),
SizedBox(height: 20),
Wrap(
spacing: 10,
children: _colors.map((color) {
return GestureDetector(
onTap: () {
setState(() {
_selectedColor = color;
});
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(
color: _selectedColor == color
? Colors.black
: Colors.transparent,
width: 3,
),
),
),
);
}).toList(),
),
],
),
);
}
}
案例3:倒计时器
import 'dart:async';
class CountdownTimer extends StatefulWidget {
_CountdownTimerState createState() => _CountdownTimerState();
}
class _CountdownTimerState extends State<CountdownTimer> {
int _seconds = 60;
Timer? _timer;
bool _isRunning = false;
void _startTimer() {
if (_isRunning) return;
setState(() {
_isRunning = true;
});
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
if (_seconds > 0) {
_seconds--;
} else {
_stopTimer();
}
});
});
}
void _stopTimer() {
_timer?.cancel();
setState(() {
_isRunning = false;
});
}
void _resetTimer() {
_stopTimer();
setState(() {
_seconds = 60;
});
}
void dispose() {
_timer?.cancel(); // 组件销毁时取消定时器
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('倒计时')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$_seconds',
style: TextStyle(
fontSize: 80,
fontWeight: FontWeight.bold,
color: _seconds <= 10 ? Colors.red : Colors.black,
),
),
SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _isRunning ? null : _startTimer,
child: Text('开始'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: _isRunning ? _stopTimer : null,
child: Text('暂停'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: _resetTimer,
child: Text('重置'),
),
],
),
],
),
),
);
}
}
常见问题
1. 什么时候用 StatefulWidget?
需要用的场景:
- 界面需要根据用户操作更新
- 需要保存用户输入的数据
- 需要定时器、动画等
- 需要网络请求后更新界面
不需要用的场景:
- 纯展示的静态页面
- 不会改变的配置信息
- 只是组合其他组件
2. setState() 里应该放什么?
// ✅ 推荐:只放修改状态的代码
setState(() {
count++;
});
// ❌ 不推荐:放复杂的计算
setState(() {
// 大量计算...
count = complexCalculation();
});
// ✅ 正确做法:先计算,再 setState
int newCount = complexCalculation();
setState(() {
count = newCount;
});
3. 为什么界面没有更新?
原因1:忘记用 setState
// ❌ 错误
count++; // 界面不会更新
// ✅ 正确
setState(() {
count++;
});
原因2:修改了对象内部属性
// ❌ 错误
myList.add('item'); // 界面不会更新
// ✅ 正确
setState(() {
myList.add('item');
});
4. 如何在 initState 中使用 context?
void initState() {
super.initState();
// ❌ 错误:initState 中不能直接用 context
// showDialog(context: context, ...);
// ✅ 正确:延迟到下一帧执行
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog(context: context, builder: (context) => AlertDialog());
});
}
性能优化技巧
1. 避免不必要的 setState
// ❌ 不好:每次都 setState
void updateData(String value) {
setState(() {
data = value;
});
}
// ✅ 更好:只在数据真正改变时 setState
void updateData(String value) {
if (data != value) {
setState(() {
data = value;
});
}
}
2. 拆分大组件
// ❌ 不好:整个页面都在一个 StatefulWidget
class BigPage extends StatefulWidget { ... }
// ✅ 更好:拆分成多个小组件
class BigPage extends StatefulWidget {
Widget build(BuildContext context) {
return Column(
children: [
Header(), // 独立的组件
Content(), // 独立的组件
Footer(), // 独立的组件
],
);
}
}
3. 使用 const 构造函数
// ✅ 好:使用 const,Flutter 会复用组件
const Text('标题')
const Icon(Icons.home)
完整示例:综合应用
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'StatefulWidget 示例',
theme: ThemeData(primarySwatch: Colors.blue),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _counter = 0;
bool _isVisible = true;
String _selectedItem = '选项1';
void initState() {
super.initState();
print('页面初始化');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('有状态组件示例'),
),
body: SingleChildScrollView(
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// 计数器
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Text('计数器', style: TextStyle(fontSize: 20)),
SizedBox(height: 10),
Text('$_counter', style: TextStyle(fontSize: 40)),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('+'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () => setState(() => _counter--),
child: Text('-'),
),
SizedBox(width: 10),
ElevatedButton(
onPressed: () => setState(() => _counter = 0),
child: Text('重置'),
),
],
),
],
),
),
),
SizedBox(height: 20),
// 显示/隐藏
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Text('显示控制', style: TextStyle(fontSize: 20)),
SizedBox(height: 10),
if (_isVisible)
Container(
padding: EdgeInsets.all(20),
color: Colors.blue.withOpacity(0.2),
child: Text('我是可以隐藏的内容'),
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () => setState(() => _isVisible = !_isVisible),
child: Text(_isVisible ? '隐藏' : '显示'),
),
],
),
),
),
SizedBox(height: 20),
// 下拉选择
Card(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
Text('下拉选择', style: TextStyle(fontSize: 20)),
SizedBox(height: 10),
DropdownButton<String>(
value: _selectedItem,
isExpanded: true,
items: ['选项1', '选项2', '选项3'].map((item) {
return DropdownMenuItem(
value: item,
child: Text(item),
);
}).toList(),
onChanged: (value) {
setState(() => _selectedItem = value!);
},
),
SizedBox(height: 10),
Text('你选择了:$_selectedItem'),
],
),
),
),
],
),
),
);
}
void dispose() {
print('页面销毁');
super.dispose();
}
}
总结
StatefulWidget 的核心要点:
- 结构 - 由 StatefulWidget + State 两部分组成
- 更新 - 用
setState()触发界面重建 - 生命周期 - initState → build → dispose
- 使用场景 - 任何需要动态更新的界面
记住这个公式:
数据改变 + setState() = 界面更新
现在你已经掌握了 Flutter 中最重要的概念之一,去创建你的动态应用吧!
更多推荐

所有评论(0)