scoped_model 是 Google 推荐使用的应用状态管理库(Simple app state management),固然,除了它,还有其余选择,好比 Redux,Rx,hooks 等等。git
咱们说 scoped_model 用于应用状态管理,是由于除了**应用状态(app state)**之外,还有临时状态(ephemeral state)。github
也能够称为 UI 状态或者本地状态,是一种能够包含在单个 widget 的状态。web
好比如下定义:express
PageView
当前的页面BottomNavigationBar
当前选中项使用临时状态,咱们不须要使用状态管理,只须要一个 StatefulWidget
。markdown
须要在应用中多个组件之间共享,并且在不一样的用户会话之间保持,这就是应用状态,有时候也称为共享状态。app
好比如下定义:less
临时状态和应用状态之间没有很是明确的界限,好比你可能须要持久化保存 BottomNavigationBar
的选中项,那它就不是临时状态了,而是应用状态。若是你愿意,你甚至能够不用任何状态管理库,只使用 State
和 setState()
来管理应用全部的状态。ide
scoped_model 由三个部分组成,ScopedModel
、ScopedModelDescendant
和 Model
。工具
Model
是定义一个数据模型的基类,它继承了 Listenable
,提供了 notifyListeners()
方法来通知组件须要更新。oop
@protected
void notifyListeners() {
// We schedule a microtask to debounce multiple changes that can occur
// all at once.
if (_microtaskVersion == _version) {
// 去抖
_microtaskVersion++;
// 安排一个 Microtask 任务
scheduleMicrotask(() {
_version++;
_microtaskVersion = _version;
// Convert the Set to a List before executing each listener. This
// prevents errors that can arise if a listener removes itself during
// invocation!
_listeners.toList().forEach((VoidCallback listener) => listener());
});
}
}
复制代码
关于 Microtask:这涉及到 dart 的单线程模型,这里咱们只须要知道,它的优先级比 event 要高,会先执行。
关于更多信息,能够查看 event-loop
在定义一个 Model
后,咱们须要再定义一个 ScopedModel
做为 root widget,它有两个参数,一个是 model
即咱们上面定义的 Model
实例,另一个是 child
,则是咱们定义的 widget。ScopedModel
是一个 StetelessWidget
,咱们看下它的 build()
方法:
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: model,
builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);
}
复制代码
AnimatedBuilder
是一个用于构建动画的 widget,它的 animation
参数是一个 Listenable
类:
abstract class Listenable {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const Listenable();
/// Return a [Listenable] that triggers when any of the given [Listenable]s
/// themselves trigger.
///
/// The list must not be changed after this method has been called. Doing so
/// will lead to memory leaks or exceptions.
///
/// The list may contain nulls; they are ignored.
factory Listenable.merge(List<Listenable> listenables) = _MergingListenable;
/// Register a closure to be called when the object notifies its listeners.
void addListener(VoidCallback listener);
/// Remove a previously registered closure from the list of closures that the
/// object notifies.
void removeListener(VoidCallback listener);
}
复制代码
AnimatedBuilder
会调用 addListener()
方法添加一个监听者,而后调用 setState()
方法进行 rebuild。从上面可知,Model
继承 Listenable
类。这也是为何在修改值后须要调用 notifyListeners()
的缘由。
再看下 builder
参数,它实际上返回了一个 _InheritedModel
实例:
class _InheritedModel<T extends Model> extends InheritedWidget {
final T model;
final int version;
_InheritedModel({Key key, Widget child, T model})
: this.model = model,
this.version = model._version,
super(key: key, child: child);
@override
bool updateShouldNotify(_InheritedModel<T> oldWidget) =>
(oldWidget.version != version);
}
复制代码
InheritedWidget
是 scoped_model
的核心。
InheritedWidget
能够在组件树中有效的传递和共享数据。将 InheritedWidget
做为 root widget,child widget 能够经过 inheritFromWidgetOfExactType()
方法返回距离它最近的 InheritedWidget
实例,同时也将它注册到 InheritedWidget
中,当 InheritedWidget
的数据发生变化时,child widget 也会随之 rebuild。
当 InheritedWidget
rebuild 时,会调用 updateShouldNotify()
方法来决定是否重建 child widget。
继续看 ScopedModel
,它使用 version
来判断是否须要通知 child widget 更新:
@override
bool updateShouldNotify(_InheritedModel<T> oldWidget) =>
(oldWidget.version != version);
复制代码
当咱们调用 Model
的 notifyListeners()
方法时,version
就会自增。
ScopedModelDescendant
是一个工具类,用于获取指定类型的 Model
,当 Model
更新时,会从新执行 build()
方法:
@override
Widget build(BuildContext context) {
return builder(
context,
child,
ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange),
);
}
static T of<T extends Model>(
BuildContext context, {
bool rebuildOnChange = false,
}) {
final Type type = _type<_InheritedModel<T>>();
// 最终也是使用 inheritFromWidgetOfExactType 或 ancestorWidgetOfExactType
Widget widget = rebuildOnChange
? context.inheritFromWidgetOfExactType(type)
: context.ancestorWidgetOfExactType(type);
if (widget == null) {
throw new ScopedModelError();
} else {
return (widget as _InheritedModel<T>).model;
}
}
static Type _type<T>() => T;
复制代码
注意到,在调用 ScopedModel.of()
方法时,有个 rebuildOnChange
参数,表示当 Model
更新时,是否须要 rebuild。当设置为 false
时,会使用 ancestorWidgetOfExactType()
方法去获取最近的 InheritedWidget
,和 inheritFromWidgetOfExactType()
方法的区别是,inheritFromWidgetOfExactType
在获取的同时会注册到 InheritedWidget
上。
使用 scoped_model
,咱们首先定义一个 Model
,这里面封装了对数据的操做,须要注意,数据改变后须要调用 notifyListeners()
方法。接着再将 ScopedModel
做为 root widget,传递一个 Model
实例,最后咱们可使用 ScopedModelDescendant
来响应数据的修改,也能够手动调用 ScopedModel.of()
方法来获取 Model
实例,调用这个方法,若是参数 rebuildOnChange
传递为 true
,则同时会将当前 widget 注册到 InheritedWidget
上,当数据改变时,当前 widget 会从新构建。