scoped-model源码解析

参考

scoped_model 是 Google 推荐使用的应用状态管理库(Simple app state management),固然,除了它,还有其余选择,好比 Redux,Rx,hooks 等等。git

临时状态和应用状态

咱们说 scoped_model 用于应用状态管理,是由于除了**应用状态(app state)**之外,还有临时状态(ephemeral state)。github

临时状态

也能够称为 UI 状态或者本地状态,是一种能够包含在单个 widget 的状态。web

好比如下定义:express

  • PageView 当前的页面
  • 动画当前的进度
  • BottomNavigationBar 当前选中项

使用临时状态,咱们不须要使用状态管理,只须要一个 StatefulWidgetmarkdown

应用状态

须要在应用中多个组件之间共享,并且在不一样的用户会话之间保持,这就是应用状态,有时候也称为共享状态。app

好比如下定义:less

  • 用户偏好设置
  • 登陆信息
  • 通知栏功能
  • 购物车功能
  • 已读未读功能

临时状态和应用状态之间没有很是明确的界限,好比你可能须要持久化保存 BottomNavigationBar 的选中项,那它就不是临时状态了,而是应用状态。若是你愿意,你甚至能够不用任何状态管理库,只使用 StatesetState() 来管理应用全部的状态。ide

scoped_model

scoped_model 由三个部分组成,ScopedModelScopedModelDescendantModel工具

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

ScopedModel

在定义一个 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);                                
}                                                                    
复制代码

InheritedWidgetscoped_model 的核心。

InheritedWidget

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); 
复制代码

当咱们调用 ModelnotifyListeners() 方法时,version 就会自增。

ScopedModelDescendant

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 会从新构建。

原文地址

相关文章
相关标签/搜索