上一篇中介绍了它的使用,简单分为三步 首先新建继承于Model的类,在其中添加notifyListeners方法,其次在顶层用ScopedModel来包裹,并把刚才创建好的model传递进来,最后在使用的地方调用ScopedModelDescendant或者ScopedModel.of(context)来使用。所以我们就看看这三步中每一步都干了什么。(版本1.0.1)
Model类:源码如下
abstract class Model extends Listenable {
final Set<VoidCallback> _listeners = Set<VoidCallback>();
int _version = 0;
int _microtaskVersion = 0;
/// [listener] will be invoked when the model changes.
@override
void addListener(VoidCallback listener) {
_listeners.add(listener);
}
/// [listener] will no longer be invoked when the model changes.
@override
void removeListener(VoidCallback listener) {
_listeners.remove(listener);
}
/// Returns the number of listeners listening to this model.
int get listenerCount => _listeners.length;
/// Should be called only by [Model] when the model has changed.
@protected
void notifyListeners() {
// We schedule a microtask to debounce multiple changes that can occur
// all at once.
if (_microtaskVersion == _version) {
_microtaskVersion++;
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());
});
}
}
}
首先这个类继承于Listenable,而这个listenable官方给出的解释就是
/// An object that maintains a list of listeners.
//他维护一个listener的集合
/// The listeners are typically used to notify clients that the object has been updated
///这些集合一般用来通知客户端,当前的对象有了更新
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);
}
他提供了添加观察和移除观察的方法,正好是观察者模式所需要的。至于model类中的add和remove什么时候调用我们稍后会说。而notifyListeners是我们自己手动调用的。可以看到这里主要是对version值进行了更改,并且调用了每个listener中的VoidCallback。到目前为止我们只知道通过notifyListener方法发送更新,其余的还不清除,那我们接着看第二步
ScopedModel:在顶层使用这个类包裹刚才声明的model
/// Provides a [Model] to all descendants of this Widget.
///为当前widget的子组件提供model,所以我们之前把这个放在materialApp上
/// Descendant Widgets can access the model by using the
/// [ScopedModelDescendant] Widget, which rebuilds each time the model changes,
/// or directly via the [ScopedModel.of] static method.
///
/// To provide a Model to all screens, place the [ScopedModel] Widget above the
/// [WidgetsApp] or [MaterialApp] in the Widget tree.
///
/// ### Example
///
/// ```
/// ScopedModel<CounterModel>(
/// model: CounterModel(),
/// child: ScopedModelDescendant<CounterModel>(
/// builder: (context, child, model) => Text(model.counter.toString()),
/// ),
/// );
/// ```
class ScopedModel<T extends Model> extends StatelessWidget {
/// The [Model] to provide to [child] and its descendants.
final T model;
/// The [Widget] the [model] will be available to.
final Widget child;
ScopedModel({@required this.model, @required this.child})
: assert(model != null),
assert(child != null);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: model,
builder: (context, _) => _InheritedModel<T>(model: model, child: child),
);
}
可以看到他其实是一个StatelessWidget,将构建页面的model以及页面child利用AnimatedBuilder进行了组装。我们先来看看AnimatedBuilder是个什么东西。
class AnimatedBuilder extends AnimatedWidget {
/// Creates an animated builder.
///
/// The [animation] and [builder] arguments must not be null.
const AnimatedBuilder({
Key key,
@required Listenable animation,
@required this.builder,
this.child,
}) : assert(animation != null),
assert(builder != null),
super(key: key, listenable: animation);
/// Called every time the animation changes value.
final TransitionBuilder builder;
/// The child widget to pass to the [builder].
///
/// If a [builder] callback's return value contains a subtree that does not
/// depend on the animation, it's more efficient to build that subtree once
/// instead of rebuilding it on every animation tick.
///
/// If the pre-built subtree is passed as the [child] parameter, the
/// [AnimatedBuilder] will pass it back to the [builder] function so that it
/// can be incorporated into the build.
///
/// Using this pre-built child is entirely optional, but can improve
/// performance significantly in some cases and is therefore a good practice.
final Widget child;
@override
Widget build(BuildContext context) {
return builder(context, child);
}
}
它继承于AnimatedWidget,可以看到其中的必传参数为listenable以及builder(Widget Function(BuildContext context, Widget child);),然后只在build方法中利用传入的builder构建child,我们再继续看一下AnimatedWidget
abstract class AnimatedWidget extends StatefulWidget {
/// Creates a widget that rebuilds when the given listenable changes.
///
/// The [listenable] argument is required.
const AnimatedWidget({
Key key,
@required this.listenable,
}) : assert(listenable != null),
super(key: key);
/// The [Listenable] to which this widget is listening.
///
/// Commonly an [Animation] or a [ChangeNotifier].
final Listenable listenable;
/// Override this method to build widgets that depend on the state of the
/// listenable (e.g., the current value of the animation).
@protected
Widget build(BuildContext context);
/// Subclasses typically do not override this method.
@override
_AnimatedState createState() => _AnimatedState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<Listenable>('animation', listenable));
}
}
class _AnimatedState extends State<AnimatedWidget> {
@override
void initState() {
super.initState();
widget.listenable.addListener(_handleChange);
}
@override
void didUpdateWidget(AnimatedWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.listenable != oldWidget.listenable) {
oldWidget.listenable.removeListener(_handleChange);
widget.listenable.addListener(_handleChange);
}
}
@override
void dispose() {
widget.listenable.removeListener(_handleChange);
super.dispose();
}
void _handleChange() {
setState(() {
// The listenable's state is our build state, and it changed already.
});
}
@override
Widget build(BuildContext context) => widget.build(context);
}
可以看到AnimatedWidget是一个StatefulWidget,在initState时候调用了传入listenable的addListener方法,在dispose的时候调用了removeListener方法,而添加和移除的listener则为_handleChange()这个方法,也就是调用了setState,使其重新build。所以之前分析的在notifyListeners()方法中调用每个listener的VoidCallback其实就是这个_handleChange()。到这里就可以清楚的知道如何的add、remove、notify listener了。回过来还看ScopedModel这个类,刚才我们看到了AnimatedBuilder,他其中的builder是用_InheritedModel构建的,我们来看看
/// Provides [model] to its [child] [Widget] tree via [InheritedWidget]. When
/// [version] changes, all descendants who request (via
/// [BuildContext.inheritFromWidgetOfExactType]) to be rebuilt when the model
/// changes will do so.
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);
}
可以看到之前在model中定义的version派上用场了,也就是说如果两次的version如果一样那么就不触发更新。而这个InheritedWidget官方解释为
/// Base class for widgets that efficiently propagate information down the tree.
///widget的基类,可以有效的沿着widget树进行消息的传递
/// To obtain the nearest instance of a particular type of inherited widget from
/// a build context, use [BuildContext.inheritFromWidgetOfExactType].
///为了获取某一指定类型的inheritedWidget的实例,可以通过buildContext.inheritFromWidgetOfExactType
/// Inherited widgets, when referenced in this way, will cause the consumer to
/// rebuild when the inherited widget itself changes state.
/// 通过这种方式引用的widget,当他本身的状态发生改变时会让它的消费者重新build
至此前两步就分析完了,最后我们来看最后一步
ScopedModelDescendant:他的源码如下
/// Finds a specific [Model] provided by a [ScopedModel] Widget and rebuilds
/// whenever the [Model] changes.
///
/// Provides an option to disable rebuilding when the [Model] changes.
///
/// Provide a constant [child] Widget if some portion inside the builder does
/// not rely on the [Model] and should not be rebuilt.
///
/// ### Example
///
/// ```
/// ScopedModel<CounterModel>(
/// model: CounterModel(),
/// child: ScopedModelDescendant<CounterModel>(
/// child: Text('Button has been pressed:'),
/// builder: (context, child, model) {
/// return Column(
/// children: [
/// child,
/// Text('${model.counter}'),
/// ],
/// );
/// }
/// ),
/// );
/// ```
class ScopedModelDescendant<T extends Model> extends StatelessWidget {
/// Called whenever the [Model] changes.
final ScopedModelDescendantBuilder<T> builder;
/// An optional constant child that does not depend on the model. This will
/// be passed as the child of [builder].
final Widget child;
/// An optional constant that determines whether the
final bool rebuildOnChange;
/// Constructor.
ScopedModelDescendant({
@required this.builder,
this.child,
this.rebuildOnChange = true,
});
@override
Widget build(BuildContext context) {
return builder(
context,
child,
ScopedModel.of<T>(context, rebuildOnChange: rebuildOnChange),
);
}
}
可以看到这里一共就三个参数,builder,child,和rebuildOnChange(变化时是否重建)。这里的builder定义如下
typedef Widget ScopedModelDescendantBuilder<T extends Model>(
BuildContext context,
Widget child,
T model,
);
其实就是利用定义好的Model以及child构建页面,这里重点看一下ScopeModel.of方法
static T of<T extends Model>(
BuildContext context, {
bool rebuildOnChange = false,
}) {
final Type type = _type<_InheritedModel<T>>();
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;
}
这里首先获取了一下它的 Type,随后根据 rebuildOnChange 的值用 inheritFromWidgetOfExactType/ ancestorWidgetOfExactType来查找widget。 inheritFromWidgetOfExactType 是用来获取给定类型的最近的 widget,并且在值更新的时候自动重新构建。 ancestorWidgetOfExactType 是用来获取给定类型最近的 祖先 Widget,并且在值更新的时候不重新构建。 所以这样就控制住了没有必要的UI更新