setState
setState() 是 Flutter 最基础的状态更新方式,用于在 StatefulWidget 的 State 类中通知框架状态已变更。
基本用法
dart
import 'package:flutter/material.dart';
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
void _increment() {
// ─── ★ setState 修改状态 ──────────────
setState(() {
_count++; // 在回调中修改状态
});
// ─── ☆ setState 修改状态 ──────────────
// setState 调用后,build() 会被重新执行
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('setState 示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_count', style: const TextStyle(fontSize: 48)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _increment,
child: const Text('+1'),
),
],
),
),
);
}
}工作原理
用户操作 → 调用 setState() → 框架标记dirty → 框架调用 build() → UI 更新- 你调用
setState() - 框架将当前 State 标记为「dirty」
- 在下一帧,框架调用
build()重新构建 Widget 树 - 框架对比新旧 Widget 树,只更新变化的部分
注意事项
不要在 setState 中执行耗时操作
dart
// ❌ 错误
void _loadData() {
setState(() {
final data = http.get(Uri.parse(url)); // 耗时操作!
_items = data;
});
}
// ✅ 正确
void _loadData() async {
setState(() => _isLoading = true); // 先显示 loading
final data = await http.get(Uri.parse(url));
setState(() {
_items = data;
_isLoading = false;
});
}不要在 build 中调用 setState
dart
// ❌ 错误:会导致无限循环
@override
Widget build(BuildContext context) {
setState(() { _count++; });
return Text('$_count');
}
// ✅ 正确:使用 WidgetsBinding.addPostFrameCallback
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) setState(() { _count++; });
});
}mounted 检查
在异步操作后调用 setState 前,检查组件是否还在 Widget 树中:
dart
import 'package:flutter/material.dart';
class MountedCheckPage extends StatefulWidget {
const MountedCheckPage({super.key});
@override
State<MountedCheckPage> createState() => _MountedCheckPageState();
}
class _MountedCheckPageState extends State<MountedCheckPage> {
String _data = '等待加载';
void _fetchData() async {
final result = await Future.delayed(const Duration(seconds: 2));
// ─── ★ mounted 检查 ──────────────
if (!mounted) return;
// ─── ☆ mounted 检查 ──────────────
setState(() => _data = '结果: $result');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('mounted 检查')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_data, style: const TextStyle(fontSize: 20)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _fetchData,
child: const Text('加载数据'),
),
],
),
),
);
}
}只更新部分 UI
dart
import 'package:flutter/material.dart';
class PartialUpdatePage extends StatefulWidget {
const PartialUpdatePage({super.key});
@override
State<PartialUpdatePage> createState() => _PartialUpdatePageState();
}
class _PartialUpdatePageState extends State<PartialUpdatePage> {
bool _isLoading = false;
String _data = '';
void _load() async {
// ─── ★ 先设 loading ──────────────
setState(() => _isLoading = true);
// ─── ☆ 先设 loading ──────────────
await Future.delayed(const Duration(seconds: 2));
if (!mounted) return;
setState(() {
_data = '加载完成';
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('setState 局部更新')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (_isLoading)
const CircularProgressIndicator()
else
Text(_data.isEmpty ? '点击加载' : _data),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _load,
child: const Text('加载'),
),
],
),
),
);
}
}setState 的局限性
| 局限 | 说明 |
|---|---|
| 仅限当前 State | 无法在兄弟/跨层级组件间共享状态 |
| 每次全量重建 | build() 整体重建,大型 Widget 树性能较差 |
| 状态传递麻烦 | 深层传递需要层层回调(callback hell) |
何时使用 setState
| 场景 | 推荐方案 |
|---|---|
| 单个组件的简单状态 | setState |
| 状态需要跨组件共享 | Provider / Riverpod |
| 全局状态(主题、语言、登录状态) | Provider / Riverpod / Bloc |
| 复杂业务逻辑 | Bloc / Riverpod |
TIP
对于新手,先用 setState 理解状态管理的基本概念。当项目变大、状态共享需求增多时,再迁移到 Provider 等方案。
