scoped-model源码解析

栏目: IOS · 发布时间: 5年前

内容简介:scoped_model 是 Google 推荐使用的应用状态管理库(Simple app state management),当然,除了它,还有其他选择,比如 Redux,Rx,hooks 等等。我们说 scoped_model 用于也可以称为 UI 状态或者本地状态,是一种可以包含在单个 widget 的状态。

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

临时状态和应用状态

我们说 scoped_model 用于 应用状态 管理,是因为除了**应用状态(app state)**以外,还有临时状态(ephemeral state)。

临时状态

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

比如以下定义:

PageView
BottomNavigationBar

使用临时状态,我们不需要使用状态管理,只需要一个 StatefulWidget

应用状态

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

比如以下定义:

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

临时状态和应用状态之间没有非常明确的界限,比如你可能需要持久化保存 BottomNavigationBar 的选中项,那它就不是临时状态了,而是应用状态。如果你愿意,你甚至可以不用任何状态管理库,只使用 StatesetState() 来管理应用所有的状态。

scoped_model

scoped_model 由三个部分组成, ScopedModelScopedModelDescendantModel

Model

Model 是定义一个数据模型的基类,它继承了 Listenable ,提供了 notifyListeners() 方法来通知组件需要更新。

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

原文地址


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

共享经济时代

共享经济时代

雷切尔·博茨曼、路·罗杰斯 / 唐朝文 / 上海交通大学出版社 / 2015-6-1 / 38

“共享经济”(sharing economy),也被称为“协同消费”(collaborative consumption),是在互联网上兴起的一种全新的商业模式。简单地说,消费者可以通过合作的方式来和他人共同享用产品和服务,而无需持有产品与服务的所有权。使用但不拥有,分享替代私有,即“我的就是你的”。 当下,全球经济正呈现出这样一种前所未有的趋势:消费者之间的分享、交换、借贷、租赁等共享经济......一起来看看 《共享经济时代》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具