Flutter State Management狀態管理全面分析
上期咱們對Flutter的狀態管理有了全局的認知,也知道了如何分辨是非好壞,不知道也不要緊哦,咱們接下來還會更加詳細的分析,經過閱讀Provider源碼,來看看框架到底如何組織的,是如何給咱們提供便利的。設計模式
經過官方咱們已經知道其實Provider就是對InheritedWidget的包裝,只是讓InheritedWidget用起來更加簡單且高可複用。咱們也知道它有一些缺點,如緩存
我特別想弄明白,這些缺點在Provider的設計中是如何規避的,還有一個是Stream不會主動的close掉流的通道,不得不結合StatefulWidget使用,而Provider提供了dispose回調,你能夠在該函數中主動關閉,好厲害,如何作到的呢?帶着這些疑問,咱們去尋找答案網絡
咱們先來使用它,而後在根據用例分析源碼,找到咱們想要的答案,先看一個簡單的例子app
第一步定義一個ChangeNotifier,來負責數據的變化通知框架
class Counter with ChangeNotifier { int _count = 0; int get count => _count; void increment() { _count++; notifyListeners(); } }
第二步,用ChangeNotifierProvider來訂閱Counter,不難猜出,ChangeNotifierProvider確定是InheritedWidget的包裝類,負責將Counter的狀態共享給子Widget,我這裏將ChangeNotifierProvider放到了Main函數中,並在整個Widget樹的頂端,固然這裏是個簡單的例子,我這麼寫問題不大,但你要考慮,若是是特別局部的狀態,請將ChangeNotifierProvider放到局部的地方而不是全局,但願你能明白個人用意less
void main() { runApp( /// Providers are above [MyApp] instead of inside it, so that tests /// can use [MyApp] while mocking the providers MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => Counter()), ], child: MyApp(), ), ); }
第三步,接收數據經過Consumer<Counter>,Consumer是個消費者,它負責消費ChangeNotifierProvider生產的數據ide
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return const MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('MyHomePage build'); return Scaffold( appBar: AppBar( title: const Text('Example'), ), body: Center( child: Column( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), /// Extracted as a separate widget for performance optimization. /// As a separate widget, it will rebuild independently from [MyHomePage]. /// /// This is totally optional (and rarely needed). /// Similarly, we could also use [Consumer] or [Selector]. Consumer<Counter>( builder: (BuildContext context, Counter value, Widget child) { return Text('${value.count}'); }, ), OtherWidget(), const OtherWidget2() ], ), ), floatingActionButton: FloatingActionButton( /// Calls `context.read` instead of `context.watch` so that it does not rebuild /// when [Counter] changes. onPressed: () => context.read<Counter>().increment(), tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
經過這個例子,能夠判斷出Provider封裝的足夠易用,並且Counter做爲Model層使用的with ChangeNotifier 而不是extends ,因此說侵入性也比較低,感受還不錯,那麼InheritedWidget的缺點它規避了嗎?函數
咱們多加兩個子WIdget進去,排在Consumer的後面,OtherWidget什麼都不幹,不去訂閱Counter,OtherWidget2經過context.watch<Counter>().count函數監聽而不是Consumer,來看下效果同樣不,而後在build函數中都加入了print源碼分析
class OtherWidget extends StatelessWidget { const OtherWidget({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('OtherWidget build'); // Provider.of<Counter>(context); return Text( /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes. 'OtherWidget', style: Theme.of(context).textTheme.headline4); } } class OtherWidget2 extends StatelessWidget { const OtherWidget2({Key key}) : super(key: key); @override Widget build(BuildContext context) { print('OtherWidget2 build'); return Text( /// Calls `context.watch` to make [MyHomePage] rebuild when [Counter] changes. '${context.watch<Counter>().count}', style: Theme.of(context).textTheme.headline4); } }
項目運行看下效果,跑起來是這樣的
print日誌
點擊刷新後
分析結論以下:post
局部刷新確實實現了但要經過Consumer,第二個問題不支持跨頁面(route)的狀態,這個能夠肯定的說不支持,第三個問題數據是不可變的(只讀),通過這個例子能夠分辨出數據確實是可變的對吧,那麼數據是如何變化的呢?留個懸念,下面分析源碼中來看本質。
固然要想更完整的理解ChangeNotifier、ChangeNotifierProvider、Consumer的關係
請看圖
設計模式真是無處不在哈,ChangeNotifier與ChangeNotifierProvider實現了觀察者模式,ChangeNotifierProvider與Consumer又實現了生產者消費者模式,這裏不具體聊這倆個模式,若是還不瞭解,請你自行搜索學習哦。下面直接源碼分析
在包package:meta/meta.dart下,是flutter sdk的代碼,並不屬於Provider框架的一部分哦,經過下方代碼能夠看出,這是一個標準的觀察者模型,而真正的監聽者就是typedef VoidCallback = void Function(); 是dart.ui包下定義的一個函數,沒人任何返回參數的函數。ChangerNotifier實現自抽象類Listenable,經過源碼的註釋咱們看到Listenable是一個專門負責維護監聽列表的一個抽象類。
class ChangeNotifierProvider<T extends ChangeNotifier> extends ListenableProvider<T> { static void _dispose(BuildContext context, ChangeNotifier notifier) { notifier?.dispose(); } /// 使用`create`建立一個[ChangeNotifier] /// 當ChangeNotifierProvider從樹中被移除時,自動取消訂閱經過 /// notifier?.dispose(); ChangeNotifierProvider({ Key key, @required Create<T> create, bool lazy, TransitionBuilder builder, Widget child, }) : super( key: key, create: create, dispose: _dispose, lazy: lazy, builder: builder, child: child, ); /// 生成一個已存在ChangeNotifier的Provider ChangeNotifierProvider.value({ Key key, @required T value, TransitionBuilder builder, Widget child, }) : super.value( key: key, builder: builder, value: value, child: child, ); }
分析下構造
是個通用函數typedef Create<T> = T Function(BuildContext context)用於建立T類,這裏負責建立ChangeNotifier
是否懶加載
當builder存在時將不會用child作爲子Widget,追蹤到源碼實現能夠看到以下圖
builder不存在時就用child
繼承自ListenableProvider<T>,來繼續分析它的源碼
class ListenableProvider<T extends Listenable> extends InheritedProvider<T> { /// 使用 [create] 建立一個 [Listenable] 訂閱它 /// [dispose] 能夠選擇性的釋放資源當 [ListenableProvider] 被移除樹的時候 /// [create] 不能爲空 ListenableProvider({ Key key, @required Create<T> create, Dispose<T> dispose, bool lazy, TransitionBuilder builder, Widget child, }) : assert(create != null), super( key: key, startListening: _startListening, create: create, dispose: dispose, debugCheckInvalidValueType: kReleaseMode ? null : (value) { if (value is ChangeNotifier) { // ignore: invalid_use_of_protected_member ... } }, lazy: lazy, builder: builder, child: child, ); /// 生成已存在 [Listenable] 的Provider ListenableProvider.value({ Key key, @required T value, UpdateShouldNotify<T> updateShouldNotify, TransitionBuilder builder, Widget child, }) : super.value( key: key, builder: builder, value: value, updateShouldNotify: updateShouldNotify, startListening: _startListening, child: child, ); static VoidCallback _startListening( InheritedContext<Listenable> e, Listenable value, ) { value?.addListener(e.markNeedsNotifyDependents); return () => value?.removeListener(e.markNeedsNotifyDependents); } }
又繼承自InheritedProvider<T> ,別放棄,來跟我一塊兒往下看
class InheritedProvider<T> extends SingleChildStatelessWidget { /// 建立數據value並共享給子Widget /// 當 [InheritedProvider] 從樹中被釋放時,將自動釋放數據value InheritedProvider({ Key key, Create<T> create, T update(BuildContext context, T value), UpdateShouldNotify<T> updateShouldNotify, void Function(T value) debugCheckInvalidValueType, StartListening<T> startListening, Dispose<T> dispose, TransitionBuilder builder, bool lazy, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = _CreateInheritedProvider( create: create, update: update, updateShouldNotify: updateShouldNotify, debugCheckInvalidValueType: debugCheckInvalidValueType, startListening: startListening, dispose: dispose, ), super(key: key, child: child); /// 暴漏給子孫一個已存在的數據value InheritedProvider.value({ Key key, @required T value, UpdateShouldNotify<T> updateShouldNotify, StartListening<T> startListening, bool lazy, TransitionBuilder builder, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = _ValueInheritedProvider( value: value, updateShouldNotify: updateShouldNotify, startListening: startListening, ), super(key: key, child: child); InheritedProvider._constructor({ Key key, _Delegate<T> delegate, bool lazy, TransitionBuilder builder, Widget child, }) : _lazy = lazy, _builder = builder, _delegate = delegate, super(key: key, child: child); final _Delegate<T> _delegate; final bool _lazy; final TransitionBuilder _builder; @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); _delegate.debugFillProperties(properties); } @override _InheritedProviderElement<T> createElement() { return _InheritedProviderElement<T>(this); } @override Widget buildWithChild(BuildContext context, Widget child) { assert( _builder != null || child != null, '$runtimeType used outside of MultiProvider must specify a child', ); return _InheritedProviderScope<T>( owner: this, child: _builder != null ? Builder( builder: (context) => _builder(context, child), ) : child, ); } }
構造中多出來的參數
@immutable abstract class _Delegate<T> { _DelegateState<T, _Delegate<T>> createState(); void debugFillProperties(DiagnosticPropertiesBuilder properties) {} } abstract class _DelegateState<T, D extends _Delegate<T>> { _InheritedProviderScopeElement<T> element; T get value; D get delegate => element.widget.owner._delegate as D; bool get hasValue; bool debugSetInheritedLock(bool value) { return element._debugSetInheritedLock(value); } bool willUpdateDelegate(D newDelegate) => false; void dispose() {} void debugFillProperties(DiagnosticPropertiesBuilder properties) {} void build(bool isBuildFromExternalSources) {} }
這是用到了委託模式,這裏就有點相似StatefulWidget和State的關係,一樣的_DelegateState提供了相似生命週期的函數,如willUpdateDelegate更新新的委託,dispose註銷等
class _InheritedProviderScope<T> extends InheritedWidget { _InheritedProviderScope({ this.owner, @required Widget child, }) : super(child: child); final InheritedProvider<T> owner; @override bool updateShouldNotify(InheritedWidget oldWidget) { return false; } @override _InheritedProviderScopeElement<T> createElement() { return _InheritedProviderScopeElement<T>(this); } }
至此你有沒有發現一個特色,全部的函數都被_Delegate帶走了,剩下的只有Widget交給了_InheritedProviderScope,這裏設計的也很好,畢竟InheritedWidget其實也就只能作到數據共享,跟函數並無什麼關係對吧。惟一有關係的地方,我猜想就是在InheritedWidget提供的Widget中調用
一個細節 owner: this 在 buildWithChild函數中,將InheritedProvider自己傳遞給InheritedWidget,應該是爲了方便調用它的_Delegate委託類,確定是用來回調各類函數。
... 快一點了,睡了,明天再更
繼續分享,_InheritedProviderScope惟一特殊的地方,咱們發現它本身建立了一個Element實現經過覆蓋createElement函數,返回_InheritedProviderScopeElement實例,flutter三板斧 Widget、Element、RenderObject,該框架本身實現一層Element,咱們都知道Widget是配置文件只有build和rebuild以及remove from the tree,而Element做爲一層虛擬Dom,主要負責優化,優化頁面刷新的邏輯,那咱們來詳細的分析一下_InheritedProviderScopeElement,看它都作了什麼?
/// 繼承自InheritedElement,由於InheritedWidget對應的Element就是它 /// 實現 InheritedContext,InheritedContext繼承自BuildContext,多了個T範型 class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> { /// 構造函數,將Element對應的widget傳進來 _InheritedProviderScopeElement(_InheritedProviderScope<T> widget) : super(widget); /// 是否須要通知依賴的Element變動 bool _shouldNotifyDependents = false; /// 是否容許通知變動 bool _isNotifyDependentsEnabled = true; /// 第一次構建 bool _firstBuild = true; /// 是否更新newWidget的Delegate委託 bool _updatedShouldNotify = false; /// 這個變量就是控制的數據變動,在Widget變動和Element依賴變動的時候都會被設置爲true bool _isBuildFromExternalSources = false; /// 委託類的狀態(咱們猜想對了, owner: this 就是爲了拿到上層的委託類) _DelegateState<T, _Delegate<T>> _delegateState; @override _InheritedProviderScope<T> get widget => super.widget as _InheritedProviderScope<T>; @override void updateDependencies(Element dependent, Object aspect) { final dependencies = getDependencies(dependent); // once subscribed to everything once, it always stays subscribed to everything if (dependencies != null && dependencies is! _Dependency<T>) { return; } if (aspect is _SelectorAspect<T>) { final selectorDependency = (dependencies ?? _Dependency<T>()) as _Dependency<T>; if (selectorDependency.shouldClearSelectors) { selectorDependency.shouldClearSelectors = false; selectorDependency.selectors.clear(); } if (selectorDependency.shouldClearMutationScheduled == false) { selectorDependency.shouldClearMutationScheduled = true; SchedulerBinding.instance.addPostFrameCallback((_) { selectorDependency ..shouldClearMutationScheduled = false ..shouldClearSelectors = true; }); } selectorDependency.selectors.add(aspect); setDependencies(dependent, selectorDependency); } else { // subscribes to everything setDependencies(dependent, const Object()); } } @override void notifyDependent(InheritedWidget oldWidget, Element dependent) { final dependencies = getDependencies(dependent); var shouldNotify = false; if (dependencies != null) { if (dependencies is _Dependency<T>) { for (final updateShouldNotify in dependencies.selectors) { try { assert(() { _debugIsSelecting = true; return true; }()); shouldNotify = updateShouldNotify(value); } finally { assert(() { _debugIsSelecting = false; return true; }()); } if (shouldNotify) { break; } } } else { shouldNotify = true; } } if (shouldNotify) { dependent.didChangeDependencies(); } } @override void performRebuild() { if (_firstBuild) { _firstBuild = false; _delegateState = widget.owner._delegate.createState()..element = this; } super.performRebuild(); } @override void update(_InheritedProviderScope<T> newWidget) { _isBuildFromExternalSources = true; _updatedShouldNotify = _delegateState.willUpdateDelegate(newWidget.owner._delegate); super.update(newWidget); _updatedShouldNotify = false; } @override void updated(InheritedWidget oldWidget) { super.updated(oldWidget); if (_updatedShouldNotify) { notifyClients(oldWidget); } } @override void didChangeDependencies() { _isBuildFromExternalSources = true; super.didChangeDependencies(); } @override Widget build() { if (widget.owner._lazy == false) { value; // this will force the value to be computed. } _delegateState.build(_isBuildFromExternalSources); _isBuildFromExternalSources = false; if (_shouldNotifyDependents) { _shouldNotifyDependents = false; notifyClients(widget); } return super.build(); } @override void unmount() { _delegateState.dispose(); super.unmount(); } @override bool get hasValue => _delegateState.hasValue; @override void markNeedsNotifyDependents() { if (!_isNotifyDependentsEnabled) return; markNeedsBuild(); _shouldNotifyDependents = true; } @override T get value => _delegateState.value; @override InheritedWidget dependOnInheritedElement( InheritedElement ancestor, { Object aspect, }) { return super.dependOnInheritedElement(ancestor, aspect: aspect); } }
abstract class ProxyElement extends ComponentElement { @override void update(ProxyWidget newWidget) { final ProxyWidget oldWidget = widget; assert(widget != null); assert(widget != newWidget); super.update(newWidget); assert(widget == newWidget); updated(oldWidget); _dirty = true; rebuild(); } }
來看一個生命週期的圖,輔助你理解源碼的調用關係
此圖引自大佬Reactive,他記錄了很詳細的生命週期圖,感謝做者的貢獻
abstract class InheritedContext<T> extends BuildContext { /// [InheritedProvider] 當前共享的數據 /// 此屬性是延遲加載的,第一次讀取它可能會觸發一些反作用, T get value; /// 將[InheritedProvider]標記爲須要更新依賴項 /// 繞過[InheritedWidget.updateShouldNotify]並將強制rebuild void markNeedsNotifyDependents(); /// setState是否至少被調用過一次 /// [DeferredStartListening]可使用它來區分 /// 第一次監聽,在「 controller」更改後進行重建。 bool get hasValue; }
小結一下
咱們先回顧一下咱們是如何使用InheritedWidget的,爲了能讓InheritedWidget的子Widget可以刷新,咱們不得不依賴於Statefulwidget,並經過State控制刷新Element,調用setState刷新頁面,其實底層是調用的_element.markNeedsBuild() 函數,這樣咱們明白了,其實最終控制頁面的仍是Element,那麼Provider 它也巧妙的封裝了本身的_delegateState,是私有的,並無給咱們公開使用,也沒有提供相似setState,但能夠經過markNeedsNotifyDependents函數達到了和setState同樣的調用效果,同樣的都是讓全部子Widget進行重建,可咱們要的局部刷新呢?是在Consumer裏?,來吧,不要走開,沒有廣告,精彩繼續,接下來研究Consumer源碼
class Consumer<T> extends SingleChildStatelessWidget { /// 構造函數,必傳builder Consumer({ Key key, @required this.builder, Widget child, }) : assert(builder != null), super(key: key, child: child); /// 根據 [Provider<T>] 提供的value,構建的widget final Widget Function(BuildContext context, T value, Widget child) builder; @override Widget buildWithChild(BuildContext context, Widget child) { return builder( context, Provider.of<T>(context), child, ); } }
Provider.of<T>(context) 是如何獲取數據的呢?繼續看源碼
/// 調用_inheritedElementOf函數 static T of<T>(BuildContext context, {bool listen = true}) { assert(context != null); final inheritedElement = _inheritedElementOf<T>(context); if (listen) { context.dependOnInheritedElement(inheritedElement); } return inheritedElement.value; } static _InheritedProviderScopeElement<T> _inheritedElementOf<T>( BuildContext context) { _InheritedProviderScopeElement<T> inheritedElement; if (context.widget is _InheritedProviderScope<T>) { // An InheritedProvider<T>'s update tries to obtain a parent provider of // the same type. context.visitAncestorElements((parent) { inheritedElement = parent.getElementForInheritedWidgetOfExactType< _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>; return false; }); } else { inheritedElement = context.getElementForInheritedWidgetOfExactType< _InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>; } if (inheritedElement == null) { throw ProviderNotFoundException(T, context.widget.runtimeType); } return inheritedElement; }
@override T get value => _delegateState.value;
走到這其實只是實現了讀取數據,那麼數據究竟是如何刷新的呢?咱們回過頭來看下面幾段代碼
void notifyListeners() { assert(_debugAssertNotDisposed()); if (_listeners != null) { final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners); for (final VoidCallback listener in localListeners) { try { if (_listeners.contains(listener)) listener(); } catch (exception, stack) { FlutterError.reportError(FlutterErrorDetails( exception: exception, stack: stack, library: 'foundation library', context: ErrorDescription('while dispatching notifications for $runtimeType'), informationCollector: () sync* { yield DiagnosticsProperty<ChangeNotifier>( 'The $runtimeType sending notification was', this, style: DiagnosticsTreeStyle.errorProperty, ); }, )); } } } }
這個時候遍歷全部的監聽,而後執行函數listener(),這裏其實等於執行VoidCallback的實例,那這個listener究竟是哪一個函數?
前面說了觀察者就是個普通函數,而e.markNeedsNotifyDependents就是InheritedContext的一個函數,當你notifyListeners的時候執行的就是它markNeedsNotifyDependents,上面咱們知道markNeedsNotifyDependents相似setState效果,就這樣才實現了UI的刷新。
/// ListenableProvider 的靜態函數 static VoidCallback _startListening( InheritedContext<Listenable> e, Listenable value, ) { value?.addListener(e.markNeedsNotifyDependents); /// 添加觀察者 return () => value?.removeListener(e.markNeedsNotifyDependents); } /// InheritedContext 上下文 abstract class InheritedContext<T> extends BuildContext { ... void markNeedsNotifyDependents(); ... }
到此位置局部刷新是否是還沒揭開面紗?究竟是如何作的呢?跟我一塊兒尋找,首先咱們來看一個東西
@override Widget buildWithChild(BuildContext context, Widget child) { return builder( context, Provider.of<T>(context), child, ); }
Consumer經過Provider.of<T>(context)這句話咱們才能監聽到數據的對吧,並且刷新的內容也只是這一部分,咱們再看下它的實現發現了另外一個細節
static T of<T>(BuildContext context, {bool listen = true}) { assert(context != null); final inheritedElement = _inheritedElementOf<T>(context); if (listen) { context.dependOnInheritedElement(inheritedElement); } return inheritedElement.value; }
它調用了BuildContext的dependOnInheritedElement函數,這個函數作了啥?
@override InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) { ... ancestor.updateDependencies(this, aspect); return ancestor.widget; }
@override void updateDependencies(Element dependent, Object aspect) { print("updateDependencies===================dependent ${dependent.toString()}"); final dependencies = getDependencies(dependent); ... setDependencies(dependent, const Object()); ... }
/// to manage dependency values. @protected void setDependencies(Element dependent, Object value) { _dependents[dependent] = value; }
final Map<Element, Object> _dependents = HashMap<Element, Object>();
觸發updateDependencies,經過setDependencies,將Element緩存到_dependents Map中
最後經過以下代碼更新
@override void notifyDependent(InheritedWidget oldWidget, Element dependent) { print("notifyDependent===================oldWidget ${oldWidget.toString()}"); final dependencies = getDependencies(dependent); var shouldNotify = false; if (dependencies != null) { if (dependencies is _Dependency<T>) { for (final updateShouldNotify in dependencies.selectors) { try { assert(() { _debugIsSelecting = true; return true; }()); shouldNotify = updateShouldNotify(value); } finally { assert(() { _debugIsSelecting = false; return true; }()); } if (shouldNotify) { break; } } } else { shouldNotify = true; } } if (shouldNotify) { dependent.didChangeDependencies(); /// 更新方法 } }
因此說總體流程是這樣當notifyListeners的時候實際上是觸發了InheritedWidget的performRebuild,再到 build ,build後觸發 notifyClients,notifyClients觸發notifyDependent,notifyDependent這個時候經過getDependencies獲取緩存好的Element,最終肯定是否須要刷新而後調用dependent.didChangeDependencies();更新,哈哈,終於明白了,只要widget中經過Provider.of函數訂閱後,就會被InheritedWidget緩存在一個Map中,而後刷新頁面的時候,若是子Widget不在緩存的Map中,根本不會走刷新,並且若是shouldNotify變量是false也不會刷新,這個控制確定是雖然子Widget訂閱了,但它本身就是不刷新,能夠更加細粒度的控制。
至此明白
厲害嗎?還不錯哦。
其實咱們明白了它的核心原理以後,剩下的就是擴展該框架了,我目前只分析了ChangeNotifierProvider、Consumer,其實它還有不少不少,來一張圖嚇嚇你
圖片很大,請看原圖哦
看到這個圖,是否是以爲冰山一角呢?哈哈,不過還好,核心原理就是在InheritedProvider裏面,我已經帶你趟了一遍,剩下的就靠你本身了,加油。
你們還有沒有喜歡的Flutter狀態管理框架,若是你想看到更多的狀態管理框架源碼分析,請你關注我哦,若是你讀到最後,若是你以爲還不錯,也請你點個贊,感謝🙏