一、状态管理
1.概念
Flutter是一种响应式编程框架,能根据数据或状态变化自动更新用户界面。状态(State)指的是某个时间点上,应用中的所有变量和数据的当前值或配置,比如用户登录信息、表单数据、页面显示内容、网络请求的结果等。状态管理涉及如何追踪、更新和同步应用程序的状态,确保在数据变化时,用户界面能够正确更新。
2.方式
-
父 Widget 管理子 Widget 的状态:适用于跟用户数据相关的状态管理
-
Widget 管理自己的状态:适用于组件私有的状态,比如UI样式
-
父 Widget 和子 Widget混合管理:既涉及数据也涉及UI样式
-
全局状态管理:需要同步跨组件状态
-
事件通知+setState:Flutter 框架重新调用 build 方法,对比新的和旧的 widget 树,重建发生了变化的部分
-
InheritedWidget:在整个组件树中传递数据,通知依赖它的子树,当数据发生变化时重新构建
-
二、setState
只能在 StatefulWidget 中使用,适用于局部状态更新
-
源码
@protected
void setState(VoidCallback fn) { //传入一个回调函数,更新有变化的状态变量
assert(fn != null); //断言检查有传入的回调函数
assert(() { //断言检查调用 setState 时的生命周期正确
if (_debugLifecycleState == _StateLifecycle.defunct) { //检查 State 对象是否已被销毁
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called after dispose(): $this'),
ErrorDescription(
'This error happens if you call setState() on a State object for a widget that '
'no longer appears in the widget tree (e.g., whose parent widget no longer '
'includes the widget in its build). This error can occur when code calls '
'setState() from a timer or an animation callback.',
),
ErrorHint(
'The preferred solution is '
'to cancel the timer or stop listening to the animation in the dispose() '
'callback. Another solution is to check the "mounted" property of this '
'object before calling setState() to ensure the object is still in the '
'tree.',
),
ErrorHint(
'This error might indicate a memory leak if setState() is being called '
'because another object is retaining a reference to this State object '
'after it has been removed from the tree. To avoid memory leaks, '
'consider breaking the reference to this object during dispose().',
),
]);
}
if (_debugLifecycleState == _StateLifecycle.created && !mounted) { //检查 State 对象是否刚被创建尚未插入 Widget 树中
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
"hasn't been inserted into the widget tree yet. It is not necessary to call "
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.',
),
]);
}
return true;
}());
final Object? result = fn() as dynamic; //执行传入的回调函数
assert(() { //断言检查回调函数的返回值不是 Future
if (result is Future) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() callback argument returned a Future.'),
ErrorDescription(
'The setState() method on $this was called with a closure or method that '
'returned a Future. Maybe it is marked as "async".',
),
ErrorHint(
'Instead of performing asynchronous work inside a call to setState(), first '
'execute the work (without updating the widget state), and then synchronously '
'update the state inside a call to setState().',
),
]);
}
return true;
}());
_element?.markNeedsBuild(); //标记状态有更新,需要被重建的元素
}
三、Provider
封装了 InheritedWidget 的复杂性,支持多种状态管理模式(如 ChangeNotifier、ValueNotifier 等),满足不同复杂度的需求,适用于跨组件状态共享
1.ChangeNotifier
Flutter中一个实现了 Listenable 接口的类,持有一些数据状态,提供了一个简单的机制来通知监听器状态的变化
-
常用方法
-
添加监听器:void addListener(VoidCallback listener)
-
移除监听器:void removeListener(VoidCallback listener)
-
通知所有监听器状态变化:void notifyListeners()
-
不再需要实例时释放资源:void dispose()
-
-
源码
class ChangeNotifier implements Listenable {
int _count = 0; //监听器个数
List<VoidCallback?> _listeners = List<VoidCallback?>.filled(0, null); //存储监听器回调函数
int _notificationCallStackDepth = 0; //跟踪通知调用栈的深度
int _reentrantlyRemovedListeners = 0; //跟踪在通知过程中被移除的监听器数量
bool _debugDisposed = false;//跟踪ChangeNotifier是否被销毁
bool _debugAssertNotDisposed() { //断言检查 ChangeNotifier 没有被销毁
assert(() {
if (_debugDisposed) {
throw FlutterError(
'A $runtimeType was used after being disposed.\n'
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
);
}
return true;
}());
return true;
}
@protected
bool get hasListeners { //检查是否有监听器
assert(_debugAssertNotDisposed());
return _count > 0;
}
@override
void addListener(VoidCallback listener) { //添加监听器
assert(_debugAssertNotDisposed());
if (_count == _listeners.length) {
if (_count == 0) {
_listeners = List<VoidCallback?>.filled(1, null);
} else {
final List<VoidCallback?> newListeners =
List<VoidCallback?>.filled(_listeners.length * 2, null);
for (int i = 0; i < _count; i++) {
newListeners[i] = _listeners[i];
}
_listeners = newListeners;
}
}
_listeners[_count++] = listener;
}
void _removeAt(int index) { //从监听器列表中移除指定索引的监听器,并可能缩小列表大小
_count -= 1;
if (_count * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(_count, null);
for (int i = 0; i < index; i++)
newListeners[i] = _listeners[i];
for (int i = index; i < _count; i++)
newListeners[i] = _listeners[i + 1];
_listeners = newListeners;
} else {
for (int i = index; i < _count; i++)
_listeners[i] = _listeners[i + 1];
_listeners[_count] = null;
}
}
@override
void removeListener(VoidCallback listener) { //移除监听器
assert(_debugAssertNotDisposed());
for (int i = 0; i < _count; i++) {
final VoidCallback? _listener = _listeners[i];
if (_listener == listener) {
if (_notificationCallStackDepth > 0) {
_listeners[i] = null;
_reentrantlyRemovedListeners++;
} else {
_removeAt(i);
}
break;
}
}
}
@mustCallSuper
void dispose() { //ChangeNotifier不再使用时释放资源
assert(_debugAssertNotDisposed());
assert(() {
_debugDisposed = true;
return true;
}());
}
@protected
@visibleForTesting
@pragma('vm:notify-debugger-on-exception')
void notifyListeners() { //通知所有监听器状态变化
assert(_debugAssertNotDisposed());
if (_count == 0)
return;
_notificationCallStackDepth++;
final int end = _count;
for (int i = 0; i < end; i++) {
try {
_listeners[i]?.call();
} catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'foundation library',
context: ErrorDescription('while dispatching notifications for $runtimeType'),
informationCollector: () => <DiagnosticsNode>[
DiagnosticsProperty<ChangeNotifier>(
'The $runtimeType sending notification was',
this,
style: DiagnosticsTreeStyle.errorProperty,
),
],
));
}
}
_notificationCallStackDepth--;
if (_notificationCallStackDepth == 0 && _reentrantlyRemovedListeners > 0) {
final int newLength = _count - _reentrantlyRemovedListeners;
if (newLength * 2 <= _listeners.length) {
final List<VoidCallback?> newListeners = List<VoidCallback?>.filled(newLength, null);
int newIndex = 0;
for (int i = 0; i < _count; i++) {
final VoidCallback? listener = _listeners[i];
if (listener != null) {
newListeners[newIndex++] = listener;
}
}
_listeners = newListeners;
} else {
for (int i = 0; i < newLength; i += 1) {
if (_listeners[i] == null) {
int swapIndex = i + 1;
while(_listeners[swapIndex] == null) {
swapIndex += 1;
}
_listeners[i] = _listeners[swapIndex];
_listeners[swapIndex] = null;
}
}
}
_reentrantlyRemovedListeners = 0;
_count = newLength;
}
}
}
2.ChangeNotifierProvider
provider 包中提供的一种状态管理解决方案,在组件树的某个节点上创建并提供一个 ChangeNotifier 实例,管理其生命周期和监听器
-
常用方法
-
构造:ChangeNotifierProvider()
-
required Create<T> create:必需参数,用于创建ChangeNotifier实例,Create<T> 是一个函数类型,一般接受一个 BuildContext 并返回一个 ChangeNotifier 对象
-
Widget? child:可选参数,一般包含需要访问/监听ChangeNotifier中的数据的widget
-
-
ChangeNotifierProvider.value()
-
required T value:必需参数,对应的ChangeNotifier实例
-
Widget? child:可选参数,同上
-
-
-
源码
class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {
ChangeNotifierProvider({ //默认构造函数,创建一个新的ChangeNotifier
Key? key,
required Create<T> create, //必需参数,用于创建ChangeNotifier实例,Create<T> 是一个函数类型,接受一个 BuildContext 并返回一个类型为 T 的对象
bool? lazy, //延迟创建,true则ChangeNotifier实例在第一次被访问时创建,false则在ChangeNotifierProvider创建时创建,默认为true
TransitionBuilder? builder, //用于在状态管理的基础上添加额外的 UI 逻辑
Widget? child, //子组件,一般包含需要访问/监听ChangeNotifier中的数据的widget
}) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
builder: builder,
child: child,
);
ChangeNotifierProvider.value({ //包装已有的ChangeNotifier
Key? key,
required T value, //必需参数,包含的ChangeNotifier实例
TransitionBuilder? builder,
Widget? child,
}) : super.value(
key: key,
builder: builder,
value: value,
child: child,
);
static void _dispose(BuildContext context, ChangeNotifier? notifier) { //用于释放 ChangeNotifier 资源,在 Provider 被移除时自动被调用
notifier?.dispose();
}
}
3.Consumer
一个特殊的Widget,在 ChangeNotifierProvider 的子组件内部使用,可以监听 ChangeNotifier 对象的变化,提供了一个builder回调函数,用于重建其子Widget
-
源码
class Consumer<T> extends SingleChildStatelessWidget {
Consumer({ //默认构造函数
Key? key,
required this.builder, //必需参数,构建子Widget
Widget? child, //传递一个不依赖于 ChangeNotifier 状态的子Widget,会被传递给builder回调函数
}) : super(key: key, child: child);
final Widget Function(
BuildContext context,
T value,
Widget? child, //传递一个不依赖于 ChangeNotifier 状态的子Widget,提高性能
) builder;
@override
Widget buildWithChild(BuildContext context, Widget? child) { //可以重写该函数对builder操作执行一些额外的处理
return builder(
context,
Provider.of<T>(context),
child,
);
}
}
4.使用示例
1)新建一个flutter项目,将provider包添加到 pubspec.yaml 文件的依赖管理中
dependencies:
flutter:
sdk: flutter
provider: ^6.0.0
2)创建一个需要进行数据更新的类继承 ChangeNotifier
import 'package:flutter/material.dart';
class CounterViewModel extends ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void increment() {
_counter++;
notifyListeners();
}
}
3)在页面中创建 ChangeNotifier 实例,并使用 ChangeNotifierProvider 包裹需要监听数据的 Widget
-
ChangeNotifier 实例需要多次多处调用时,使用 ChangeNotifierProvider.value() 只包含需要更新的 Widget 结构更清晰
-
ChangeNotifier 实例不需要多次使用时,可构造 ChangeNotifierProvider 创建ChangeNotifier 实例
4)使用 Consumer 包裹需要监听数据并更新 UI 的Widget
import 'package:flutter/material.dart';
import 'package:flutter_demo/demoViewModel.dart';
import 'package:provider/provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
final CounterViewModel _viewModel = CounterViewModel();
MyHomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<CounterViewModel>.value(
value: _viewModel,
child: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Consumer<CounterViewModel>( //需要根据数据状态更新UI的组件
builder: (context, viewModel, child) {
return Text(
'${_viewModel.counter}',
);
}
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _viewModel.increment(),
tooltip: 'Increment',
child: const Icon(Icons.add),
)));
}
}
1526



被折叠的 条评论
为什么被折叠?



