在应用开发过程中,状态管理是一个绕不开的话题;一般情况下,由组件管理私有的状态,如果状态需要跨组件进行共享,则需要交由共同的父组件进行管理。
当前对于跨组件状态共享的管理方式比较多,比如:全局事件总线 EventBus ,其通过观察者模式进行实现,发布者负责更新、发布状态,观察者监听状态的改变,并执行一些操作;
enum Event{
change,
}
// 状态改变后发布状态改变事件
bus.emit(Event.change);
// 状态变化处理逻辑
void onChangedHandle(e){}
// 组件状态初始化时订阅状态改变事件
@override
void initState() {
bus.on(Event.change, onChangeHandle);
super.initState();
}
// 组件销毁时主动取消订阅避免内存泄露
@override
void dispose() {
bus.off(Event.change, onChangeHandle);
super.dispose();
}
不过 EventBus 的缺点亦非常的明显,我们必须显示的定义各种事件,同时阅者必须需显式注册状态改变回调,也必须在组件销毁时手动去解绑回调以避免内存泄露;
Flutter 中 InheritedWidget 默认就提供了与子孙组件的依赖关系,并且当 InheritedWidget 数据发生变化时,可以自动更新依赖的子孙组件;因此在跨组件状态共享的时候我们只需要将状态保存在 InheritedWidget 中,并在子孙组件中引用依赖即可;
🌰
// 创建通用的 InheritedWidget,用于保存任需要跨组件共享的状态
// 由于业务数据不可预期,因此使用范型来定义通用类
class InheritedProvider<T> extends InheritedWidget {
InheritedProvider({
@required this.data,
Widget child
}) : super(child: child);
//共享状态使用泛型
final T data;
@override
bool updateShouldNotify(InheritedProvider<T> old) {
// 在此简单返回true,则每次更新都会调用依赖其的子孙节点的 `didChangeDependencies`。
return true;
}
}
通用类创建完成后,接下来就需要在数据发生变化时重新构建 InheritedProvider,这时我们就会面临两个问题:
- 数据发生变化怎么通知?
- 谁来重新构建 InheritedProvider?
首先,为了更贴近 Flutter 开发,我们使用 Flutter SDK 中提供的 ChangeNotifier 类 来实现 EventBus,它继承自Listenable,也实现了一个Flutter风格的发布者-订阅者模式
class ChangeNotifier implements Listenable {
@override
void addListener(VoidCallback listener) {
//添加监听器
}
@override
void removeListener(VoidCallback listener) {
//移除监听器
}
void notifyListeners() {
//通知所有监听器,触发监听器回调
}
...
}
接下来,我们需要将共享状态放到一个 Modal 类中,并让 Modal 类来继承 ChangeNotifier ;这样当共享的状态发生变化时,我们只需要调用 notifyListeners() 通知订阅者,然后由订阅者来创建 InheritedProvider 就可以了
// 该方法用于在Dart中获取模板类型
Type _typeOf<T>() => T;
class ChangeNotifierProvider<T extends ChangeNotifier> extends StatefulWidget {
ChangeNotifierProvider({
Key key,
this.data,
this.child,
});
final Widget child;
final T data;
//定义一个of静态方法,方便子树中的 widget 获取 inheritedProvider 中保存的共享状态(Modal)
static T of<T>(BuildContext context) {
final type = _typeOf<InheritedProvider<T>>();
final provider = context.inheritFromWidgetOfExactType(type) as InheritedProvider<T>;
return provider.data;
}
@override
_ChangeNotifierProviderState<T> createState() => _ChangeNotifierProviderState<T>();
}
class _ChangeNotifierProviderState<T extends ChangeNotifier> extends State<ChangeNotifierProvider<T>> {
void update() {
//如果数据发生变化(model类调用了notifyListeners),重新构建InheritedProvider
setState(() => {});
}
@override
void didUpdateWidget(ChangeNotifierProvider<T> oldWidget) {
//当Provider更新时,如果新旧数据不"==",则解绑旧数据监听,同时添加新数据监听
if (widget.data != oldWidget.data) {
oldWidget.data.removeListener(update);
widget.data.addListener(update);
}
super.didUpdateWidget(oldWidget);
}
@override
void initState() {
// 给model添加监听器
widget.data.addListener(update);
super.initState();
}
@override
void dispose() {
// 移除model的监听器
widget.data.removeListener(update);
super.dispose();
}
@override
Widget build(BuildContext context) {
return InheritedProvider<T>(
data: widget.data,
child: widget.child,
);
}
}
_ChangeNotifierProviderState 类的主要是 监听共享状态改变时重新构建 widget 树;不过还需注意的是 _ChangeNotifierProviderState 中调用了 setState 方法,widget.child 始终是同一个,所以在执行 build 时,InheritedProvider 的 child 引用始终是同一个widget,widget.child 并不会重新 build,相当于对 child 进行了缓存;当然如果是 ChangeNotifierProvider 父 widget 重新 build 其传人的 child 亦可能发生变化