前言:
状态管理是一个重点,可以看看。之前也有很多案例用过了已经。
一、定位
本讲聚焦 Flutter 中最基础也最核心的状态管理能力
- setState 核心:用于单个 StatefulWidget 的局部状态更新,仅触发当前 Widget 的 build 方法,适合管理组件内部状态(如按钮点击、表单输入)。
- InheritedWidget 核心:Flutter 原生的跨组件状态共享方案,通过「状态缓存+依赖收集」实现精准重建,适合共享跨层级 Widget 需要访问的状态(如用户信息、主题配置)。
- 组合使用原则:用 setState 管理局部临时状态,用 InheritedWidget 封装共享状态,通过「不可变状态+copyWith」实现状态安全更新,兼顾性能与可维护性。
1.1 setState 局部刷新原理
1.2 InheritedWidget 状态共享原理
1.3 整体状态管理基础架构
二、核心知识点详解
2.1 setState 局部刷新
2.1.1 核心概念
setState 是 StatefulWidget 中最基础的状态更新方法,本质是通过标记当前 Element 为脏状态,触发该 Widget 的 build 方法重新执行,实现局部 UI 刷新。
2.1.2 核心属性/方法说明
| 名称 | 类型 | 作用 |
|---|---|---|
setState(VoidCallback fn) | 方法 | 接收一个无返回值的回调函数,在回调中更新状态,触发 build 重建 |
mounted | 布尔属性 | 标记当前 State 是否挂载到 Widget 树中,更新状态前建议检查(避免空指针) |
2.1.3 基础案例
import 'package:flutter/material.dart';
// setState 基础使用案例
class SetStateDemo extends StatefulWidget {
const SetStateDemo({super.key});
@override
State<SetStateDemo> createState() => _SetStateDemoState();
}
class _SetStateDemoState extends State<SetStateDemo> {
// 局部状态:计数器
int _counter = 0;
// 状态更新方法
void _incrementCounter() {
// 核心:调用setState更新状态
setState(() {
_counter++; // 仅更新此状态,触发build重建
});
}
@override
Widget build(BuildContext context) {
print("SetStateDemo 执行build"); // 验证仅局部刷新
return Scaffold(
appBar: AppBar(title: const Text("setState 局部刷新")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('你点击的次数:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
2.1.4 注意事项
-
仅局部刷新:
setState只会触发当前 State 对应的 Widget 的build方法,不会刷新整个页面(性能优化点) -
避免同步耗时操作:不要在
setState回调中执行网络请求、大量计算等耗时操作(会阻塞 UI 线程) -
状态更新必须在回调内:直接修改
_counter而不调用setState,或在回调外修改状态,都不会触发 UI 刷新 -
避免不必要的重建:如果状态变化不需要刷新 UI,不要调用
setState -
检查 mounted:异步操作后更新状态时,务必检查
if (mounted),避免 Widget 已销毁仍更新状态:-
// 延后两秒挂载 void _asyncUpdate() async { await Future.delayed(const Duration(seconds: 2)); if (mounted) { // 关键:检查是否仍挂载 setState(() { _counter++; }); } }
-
2.2 InheritedWidget 跨组件状态共享
2.2.1 核心概念
InheritedWidget 是 Flutter 提供的跨组件状态共享核心组件,允许上层 Widget 将状态数据向下传递,下层任意 Widget 可通过 BuildContext 高效获取,且当状态变化时,仅通知依赖该状态的子 Widget 重建。
2.2.2 核心属性/方法说明
| 名称 | 类型 | 作用 |
|---|---|---|
updateShouldNotify | 方法(返回bool) | 对比新旧 Widget 的状态,决定是否通知依赖组件重建 |
dependOnInheritedWidgetOfExactType<T>() | 方法 | 通过 BuildContext 获取指定类型的 InheritedWidget,并建立依赖关系 |
getElementForInheritedWidgetOfExactType<T>() | 方法 | 获取 InheritedWidget 对应的 Element,不建立依赖关系(状态变化不触发重建) |
2.2.3 基础案例
import 'package:flutter/material.dart';
// 步骤1:定义共享状态模型
class UserInfo {
final String name;
final int age;
UserInfo({required this.name, required this.age});
// 不可变设计:状态更新时创建新对象
UserInfo copyWith({String? name, int? age}) {
return UserInfo(
name: name ?? this.name,
age: age ?? this.age,
);
}
}
// 步骤2:自定义InheritedWidget
class UserInheritedWidget extends InheritedWidget {
// 共享状态
final UserInfo userInfo;
// 状态更新方法(供外部调用)
final Function(UserInfo) updateUserInfo;
const UserInheritedWidget({
super.key,
required this.userInfo,
required this.updateUserInfo,
required super.child,
});
// 步骤3:提供便捷的获取方法(简化子组件调用)
static UserInheritedWidget of(BuildContext context) {
final result = context.dependOnInheritedWidgetOfExactType<UserInheritedWidget>();
assert(result != null, 'No UserInheritedWidget found in context');
return result!;
}
// 步骤4:判断是否需要通知依赖组件重建
@override
bool updateShouldNotify(UserInheritedWidget oldWidget) {
// 状态变化时返回true,触发依赖组件重建
return userInfo.name != oldWidget.userInfo.name || userInfo.age != oldWidget.userInfo.age;
}
}
// 步骤5:使用共享状态的子组件1
class UserNameWidget extends StatelessWidget {
const UserNameWidget({super.key});
@override
Widget build(BuildContext context) {
print("UserNameWidget 执行build");
// 获取共享状态
final userInfo = UserInheritedWidget.of(context).userInfo;
return Text(
'用户名:${userInfo.name}',
style: const TextStyle(fontSize: 20),
);
}
}
// 步骤6:使用共享状态的子组件2
class UserAgeWidget extends StatelessWidget {
const UserAgeWidget({super.key});
@override
Widget build(BuildContext context) {
print("UserAgeWidget 执行build");
// 获取共享状态
final userInfo = UserInheritedWidget.of(context).userInfo;
return Text(
'年龄:${userInfo.age}',
style: const TextStyle(fontSize: 20),
);
}
}
// 步骤7:顶层容器组件(管理状态+包裹InheritedWidget)
class InheritedWidgetDemo extends StatefulWidget {
const InheritedWidgetDemo({super.key});
@override
State<InheritedWidgetDemo> createState() => _InheritedWidgetDemoState();
}
class _InheritedWidgetDemoState extends State<InheritedWidgetDemo> {
late UserInfo _userInfo;
@override
void initState() {
super.initState();
_userInfo = UserInfo(name: "张三", age: 20);
}
// 更新共享状态
void _updateUser() {
setState(() {
_userInfo = _userInfo.copyWith(
name: "李四",
age: _userInfo.age + 1,
);
});
}
@override
Widget build(BuildContext context) {
print("InheritedWidgetDemo 执行build");
return Scaffold(
appBar: AppBar(title: const Text("InheritedWidget 状态共享")),
body: UserInheritedWidget( // 包裹需要共享状态的Widget树
userInfo: _userInfo,
updateUserInfo: (newUserInfo) {
setState(() {
_userInfo = newUserInfo;
});
},
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
UserNameWidget(), // 子组件1:获取用户名
SizedBox(height: 20),
UserAgeWidget(), // 子组件2:获取年龄
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: _updateUser,
child: const Icon(Icons.refresh),
),
);
}
}
2.2.4 注意事项
- 不可变状态设计:InheritedWidget 本身是不可变的,状态更新需创建新的 InheritedWidget 实例(通过 setState 实现)
- 精准重建:只有调用
dependOnInheritedWidgetOfExactType的子组件会被通知重建,其他组件不受影响 - 避免过度共享:不要把所有状态都放在一个 InheritedWidget 中,按功能拆分(避免不必要的重建)
- 上下文范围:子组件必须在 InheritedWidget 的子树中,才能通过 context 获取到共享状态
- 依赖关系:如果不需要状态变化时重建,可使用
getElementForInheritedWidgetOfExactType替代(无依赖)
三、综合应用案例
3.1 需求说明
实现一个「用户信息管理」页面:
- 顶层通过 InheritedWidget 共享用户信息(姓名、年龄、积分)
- 局部通过 setState 更新积分(局部刷新)
- 跨组件更新用户姓名和年龄(共享状态刷新)
- 不同层级的子组件都能访问并响应状态变化
3.2 完整代码
import 'package:flutter/material.dart';
// 1. 定义共享状态模型
class UserState {
final String name;
final int age;
final int score;
UserState({
required this.name,
required this.age,
required this.score,
});
// 不可变更新:创建新对象
UserState copyWith({
String? name,
int? age,
int? score,
}) {
return UserState(
name: name ?? this.name,
age: age ?? this.age,
score: score ?? this.score,
);
}
}
// 2. 自定义InheritedWidget
class UserStateProvider extends InheritedWidget {
final UserState userState;
final Function(UserState) updateUserState;
const UserStateProvider({
super.key,
required this.userState,
required this.updateUserState,
required super.child,
});
// 便捷获取方法
static UserStateProvider of(BuildContext context) {
final result = context.dependOnInheritedWidgetOfExactType<UserStateProvider>();
assert(result != null, 'No UserStateProvider found in context');
return result!;
}
// 判断是否需要通知依赖组件
@override
bool updateShouldNotify(UserStateProvider oldWidget) {
return userState.name != oldWidget.userState.name ||
userState.age != oldWidget.userState.age ||
userState.score != oldWidget.userState.score;
}
}
// 3. 局部状态组件:积分更新(仅刷新自身)
class ScoreCounter extends StatefulWidget {
const ScoreCounter({super.key});
@override
State<ScoreCounter> createState() => _ScoreCounterState();
}
class _ScoreCounterState extends State<ScoreCounter> {
void _addScore() {
// 获取共享状态
final provider = UserStateProvider.of(context);
// 局部更新:仅更新积分(setState + 共享状态更新)
setState(() {
provider.updateUserState(
provider.userState.copyWith(
score: provider.userState.score + 10,
),
);
});
}
@override
Widget build(BuildContext context) {
print("ScoreCounter 执行build");
final score = UserStateProvider.of(context).userState.score;
return Column(
children: [
const Text(
'当前积分',
style: TextStyle(fontSize: 16, color: Colors.grey),
),
Text(
'$score',
style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
ElevatedButton(
onPressed: _addScore,
child: const Text('增加10积分'),
),
],
);
}
}
// 4. 跨层级子组件:用户信息展示
class UserInfoDisplay extends StatelessWidget {
const UserInfoDisplay({super.key});
@override
Widget build(BuildContext context) {
print("UserInfoDisplay 执行build");
final userState = UserStateProvider.of(context).userState;
return Card(
margin: const EdgeInsets.all(20),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('姓名:${userState.name}', style: const TextStyle(fontSize: 18)),
Text('年龄:${userState.age}', style: const TextStyle(fontSize: 18)),
Text('积分:${userState.score}', style: const TextStyle(fontSize: 18)),
],
),
),
);
}
}
// 5. 状态更新组件:修改姓名和年龄
class UserEditWidget extends StatelessWidget {
const UserEditWidget({super.key});
@override
Widget build(BuildContext context) {
print("UserEditWidget 执行build");
final provider = UserStateProvider.of(context);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: () {
provider.updateUserState(
provider.userState.copyWith(name: "新姓名-${DateTime.now().second}"),
);
},
child: const Text('修改姓名'),
),
ElevatedButton(
onPressed: () {
provider.updateUserState(
provider.userState.copyWith(age: provider.userState.age + 1),
);
},
child: const Text('年龄+1'),
),
],
);
}
}
// 6. 顶层容器:整合所有组件
class StateManagementCombinedDemo extends StatefulWidget {
const StateManagementCombinedDemo({super.key});
@override
State<StateManagementCombinedDemo> createState() => _StateManagementCombinedDemoState();
}
class _StateManagementCombinedDemoState extends State<StateManagementCombinedDemo> {
late UserState _userState;
@override
void initState() {
super.initState();
// 初始化状态
_userState = UserState(name: "初始姓名", age: 20, score: 0);
}
// 更新共享状态的核心方法
void _updateUserState(UserState newState) {
setState(() {
_userState = newState;
});
}
@override
Widget build(BuildContext context) {
print("StateManagementCombinedDemo 执行build");
return Scaffold(
appBar: AppBar(
title: const Text("状态管理综合案例"),
centerTitle: true,
),
body: UserStateProvider( // 包裹整个子树,提供共享状态
userState: _userState,
updateUserState: _updateUserState,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
UserInfoDisplay(), // 展示状态
UserEditWidget(), // 修改姓名/年龄
SizedBox(height: 30),
ScoreCounter(), // 局部更新积分
],
),
),
);
}
}
// 7. 入口函数
void main() {
runApp(const MaterialApp(
home: StateManagementCombinedDemo(),
debugShowCheckedModeBanner: false,
));
}
3.3 运行效果说明
- 初始页面显示:姓名=初始姓名、年龄=20、积分=0
- 点击「增加10积分」:仅
ScoreCounter组件重建(局部刷新),积分数值增加 - 点击「修改姓名」:
UserInfoDisplay和UserEditWidget重建(依赖共享状态),姓名更新为带秒数的新值 - 点击「年龄+1」:依赖组件重建,年龄数值+1
- 控制台打印的 build 日志可验证:只有状态变化相关的组件才会重建,实现高效刷新