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 會從新構建。

原文地址

相關文章
相關標籤/搜索