回顧
Flutter State Management狀態(tài)管理全面分析
上期我們對Flutter的狀態(tài)管理有了全局的認(rèn)知,也知道了如何分辨是非好壞,不知道也沒關(guān)系哦,我們接下來還會更加詳細(xì)的分析,通過閱讀Provider源碼,來看看框架到底如何組織的,是如何給我們提供便利的。
本期內(nèi)容
通過官方我們已經(jīng)知道其實Provider就是對InheritedWidget的包裝,只是讓InheritedWidget用起來更加簡單且高可復(fù)用。我們也知道它有一些缺點,如
- 容易造成不必要的刷新
- 不支持跨頁面(route)的狀態(tài),意思是跨樹,如果不在一個樹中,我們無法獲取
- 數(shù)據(jù)是不可變的,必須結(jié)合StatefulWidget、ChangeNotifier或者Steam使用
我特別想弄明白,這些缺點在Provider的設(shè)計中是如何規(guī)避的,還有一個是Stream不會主動的close掉流的通道,不得不結(jié)合StatefulWidget使用,而Provider提供了dispose回調(diào),你可以在該函數(shù)中主動關(guān)閉,好厲害,如何做到的呢?帶著這些疑問,我們?nèi)ふ掖鸢?/p>
如何使用
我們先來使用它,然后在根據(jù)用例分析源碼,找到我們想要的答案,先看一個簡單的例子
step 1
第一步定義一個ChangeNotifier,來負(fù)責(zé)數(shù)據(jù)的變化通知
class Counter with ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
step 2
第二步,用ChangeNotifierProvider來訂閱Counter,不難猜出,ChangeNotifierProvider肯定是InheritedWidget的包裝類,負(fù)責(zé)將Counter的狀態(tài)共享給子Widget,我這里將ChangeNotifierProvider放到了Main函數(shù)中,并在整個Widget樹的頂端,當(dāng)然這里是個簡單的例子,我這么寫問題不大,但你要考慮,如果是特別局部的狀態(tài),請將ChangeNotifierProvider放到局部的地方而不是全局,希望你能明白我的用意
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(),
),
);
}
step 3
第三步,接收數(shù)據(jù)通過Consumer<Counter>,Consumer是個消費者,它負(fù)責(zé)消費ChangeNotifierProvider生產(chǎn)的數(shù)據(jù)
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的缺點它規(guī)避了嗎?
- 容易造成不必要的刷新(解決了嗎?)
我們多加兩個子WIdget進(jìn)去,排在Consumer的后面,OtherWidget什么都不干,不去訂閱Counter,OtherWidget2通過context.watch<Counter>().count函數(shù)監(jiān)聽而不是Consumer,來看下效果一樣不,然后在build函數(shù)中都加入了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日志

點擊刷新后


分析結(jié)論如下:
- Consumer、context.watch都可以監(jiān)聽Counter變化
- Consumer只會刷新自己
- context.watch所在子widget不管是否是const都被重建后刷新數(shù)據(jù)
- OtherWidget并沒有被重建,因為它沒有訂閱Counter
局部刷新確實實現(xiàn)了但要通過Consumer,第二個問題不支持跨頁面(route)的狀態(tài),這個可以確定的說不支持,第三個問題數(shù)據(jù)是不可變的(只讀),經(jīng)過這個例子可以分辨出數(shù)據(jù)確實是可變的對吧,那么數(shù)據(jù)是如何變化的呢?留個懸念,下面分析源碼中來看本質(zhì)。
當(dāng)然要想更完整的理解ChangeNotifier、ChangeNotifierProvider、Consumer的關(guān)系

設(shè)計模式真是無處不在哈,ChangeNotifier與ChangeNotifierProvider實現(xiàn)了觀察者模式,ChangeNotifierProvider與Consumer又實現(xiàn)了生產(chǎn)者消費者模式,這里不具體聊這倆個模式,如果還不了解,請你自行搜索學(xué)習(xí)哦。下面直接源碼分析
源碼分析
ChangeNotifier
在包package:meta/meta.dart下,是flutter sdk的代碼,并不屬于Provider框架的一部分哦,通過下方代碼可以看出,這是一個標(biāo)準(zhǔn)的觀察者模型,而真正的監(jiān)聽者就是typedef VoidCallback = void Function(); 是dart.ui包下定義的一個函數(shù),沒人任何返回參數(shù)的函數(shù)。ChangerNotifier實現(xiàn)自抽象類Listenable,通過源碼的注釋我們看到Listenable是一個專門負(fù)責(zé)維護(hù)監(jiān)聽列表的一個抽象類。

ChangeNotifierProvider
class ChangeNotifierProvider<T extends ChangeNotifier>
extends ListenableProvider<T> {
static void _dispose(BuildContext context, ChangeNotifier notifier) {
notifier?.dispose();
}
/// 使用`create`創(chuàng)建一個[ChangeNotifier]
/// 當(dāng)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,
);
}
分析下構(gòu)造
- Create<T> create
是個通用函數(shù)typedef Create<T> = T Function(BuildContext context)用于創(chuàng)建T類,這里負(fù)責(zé)創(chuàng)建ChangeNotifier - bool lazy
是否懶加載 -
TransitionBuilder builder
當(dāng)builder存在時將不會用child做為子Widget,追蹤到源碼實現(xiàn)可以看到如下圖
- Widget child
builder不存在時就用child
繼承自ListenableProvider<T>,來繼續(xù)分析它的源碼
class ListenableProvider<T extends Listenable> extends InheritedProvider<T> {
/// 使用 [create] 創(chuàng)建一個 [Listenable] 訂閱它
/// [dispose] 可以選擇性的釋放資源當(dāng) [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);
}
}
- Listenable 上面已經(jīng)分析,它是負(fù)責(zé)管理觀察者列表的抽象
- 它比子類ChangeNotifierProvider多了一個構(gòu)造參數(shù)dispose,這個函數(shù)是typedef Dispose<T> = void Function(BuildContext context, T value); 是個回調(diào),應(yīng)該是當(dāng)頁面被銷毀時觸發(fā)(等再深入了源碼才能認(rèn)證,目前只是猜測,我們繼續(xù)看)
又繼承自InheritedProvider<T> ,別放棄,來跟我一起往下看
class InheritedProvider<T> extends SingleChildStatelessWidget {
/// 創(chuàng)建數(shù)據(jù)value并共享給子Widget
/// 當(dāng) [InheritedProvider] 從樹中被釋放時,將自動釋放數(shù)據(jù)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);
/// 暴漏給子孫一個已存在的數(shù)據(jù)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,
);
}
}
構(gòu)造中多出來的參數(shù)
- T update(BuildContext context, T value) 該函數(shù)返回數(shù)據(jù)變更值value,具體實現(xiàn)在_CreateInheritedProvider類中,說白了InheritedProvider<T>是個無狀態(tài)組件對嗎?那么它要變更狀態(tài)肯定要依賴于別人,而它創(chuàng)建出一個_CreateInheritedProvider類,_CreateInheritedProvider是_Delegate的實現(xiàn)類,_Delegate就是一個狀態(tài)的代理類,來看下_Delegate具體實現(xiàn)
@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的關(guān)系,同樣的_DelegateState提供了類似生命周期的函數(shù),如willUpdateDelegate更新新的委托,dispose注銷等
- UpdateShouldNotify<T> updateShouldNotify,
void Function(T value) debugCheckInvalidValueType,
StartListening<T> startListening,
Dispose<T> dispose, 這些函數(shù)全部交給了委托類 - 最關(guān)鍵的實現(xiàn)來了,到目前位置還沒看到InheritedWidget的邏輯對吧,它來了Widget buildWithChild(BuildContext context, Widget child),我們傳入的Widget就被叫_InheritedProviderScope的類給包裹了,看下源碼
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);
}
}
至此你有沒有發(fā)現(xiàn)一個特點,所有的函數(shù)都被_Delegate帶走了,剩下的只有Widget交給了_InheritedProviderScope,這里設(shè)計的也很好,畢竟InheritedWidget其實也就只能做到數(shù)據(jù)共享,跟函數(shù)并沒有什么關(guān)系對吧。唯一有關(guān)系的地方,我猜測就是在InheritedWidget提供的Widget中調(diào)用
一個細(xì)節(jié) owner: this 在 buildWithChild函數(shù)中,將InheritedProvider本身傳遞給InheritedWidget,應(yīng)該是為了方便調(diào)用它的_Delegate委托類,肯定是用來回調(diào)各種函數(shù)。
... 快一點了,睡了,明天再更
繼續(xù)分享,_InheritedProviderScope唯一特殊的地方,我們發(fā)現(xiàn)它自己創(chuàng)建了一個Element實現(xiàn)通過覆蓋createElement函數(shù),返回_InheritedProviderScopeElement實例,flutter三板斧 Widget、Element、RenderObject,該框架自己實現(xiàn)一層Element,我們都知道Widget是配置文件只有build和rebuild以及remove from the tree,而Element作為一層虛擬Dom,主要負(fù)責(zé)優(yōu)化,優(yōu)化頁面刷新的邏輯,那我們來詳細(xì)的分析一下_InheritedProviderScopeElement,看它都做了什么?
/// 繼承自InheritedElement,因為InheritedWidget對應(yīng)的Element就是它
/// 實現(xiàn) InheritedContext,InheritedContext繼承自BuildContext,多了個T范型
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
/// 構(gòu)造函數(shù),將Element對應(yīng)的widget傳進(jìn)來
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
/// 是否需要通知依賴的Element變更
bool _shouldNotifyDependents = false;
/// 是否允許通知變更
bool _isNotifyDependentsEnabled = true;
/// 第一次構(gòu)建
bool _firstBuild = true;
/// 是否更新newWidget的Delegate委托
bool _updatedShouldNotify = false;
/// 這個變量就是控制的數(shù)據(jù)變更,在Widget變更和Element依賴變更的時候都會被設(shè)置為true
bool _isBuildFromExternalSources = false;
/// 委托類的狀態(tài)(我們猜測對了, 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);
}
}
- void update(_InheritedProviderScope<T> newWidget) 讓頁面重新build的是在這里,因為InheritedElement 繼承自ProxyElement,而ProxyElement的update函數(shù)調(diào)用了兩個函數(shù)updated(已更新完成),rebuild函數(shù)觸發(fā)重新build邏輯,下面為跟蹤到的代碼
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();
}
}
- performRebuild() 是在update觸發(fā)真正調(diào)用rebuild之后被調(diào)用
- updateDependencies、notifyDependent處理Element依賴邏輯
- update、updated處理的widget更新邏輯
- didChangeDependencies當(dāng)此State對象的依賴項更改時調(diào)用,子類很少重寫此方法,因為框架總是在依賴項更改后調(diào)用build。一些子類確實重寫了此方法,因為當(dāng)它們的依存關(guān)系發(fā)生變化時,它們需要做一些昂貴的工作(例如,網(wǎng)絡(luò)獲?。?,并且對于每個構(gòu)建而言,這些工作將太昂貴。
- build() 構(gòu)建需要的widget,Element在調(diào)用build的時候也會觸發(fā)Widget的build
- void unmount() 這里看到了_delegateState.dispose();的調(diào)用,現(xiàn)在找到了吧,當(dāng)Element從樹中移除的時候,回掉了dispose函數(shù)。
來看一個生命周期的圖,輔助你理解源碼的調(diào)用關(guān)系

此圖引自大佬Reactive,他記錄了很詳細(xì)的生命周期圖,感謝作者的貢獻(xiàn)
notifyClients 這個函數(shù)干嘛的?它是InheritedElement中實現(xiàn)的函數(shù),通過官方文檔了解到,它是通過調(diào)用Element.didChangeDependencies通知所有從屬Element此繼承的widget已更改,此方法只能在構(gòu)建階段調(diào)用,通常,在重建inherited widget時會自動調(diào)用此方法,還有就是InheritedNotifier,它是InheritedWidget的子類,在其Listenable發(fā)送通知時也調(diào)用此方法。
markNeedsNotifyDependents 如果你調(diào)用它,會強(qiáng)制build后 通知所以依賴Element刷新widget,看下面代碼,發(fā)現(xiàn)該函數(shù)在InheritedContext中定義,所以我們可以通過InheritedContext上下文來強(qiáng)制頁面的構(gòu)建
abstract class InheritedContext<T> extends BuildContext {
/// [InheritedProvider] 當(dāng)前共享的數(shù)據(jù)
/// 此屬性是延遲加載的,第一次讀取它可能會觸發(fā)一些副作用,
T get value;
/// 將[InheritedProvider]標(biāo)記為需要更新依賴項
/// 繞過[InheritedWidget.updateShouldNotify]并將強(qiáng)制rebuild
void markNeedsNotifyDependents();
/// setState是否至少被調(diào)用過一次
/// [DeferredStartListening]可以使用它來區(qū)分
/// 第一次監(jiān)聽,在“ controller”更改后進(jìn)行重建。
bool get hasValue;
}
小結(jié)一下
我們先回顧一下我們是如何使用InheritedWidget的,為了能讓InheritedWidget的子Widget能夠刷新,我們不得不依賴于Statefulwidget,并通過State控制刷新Element,調(diào)用setState刷新頁面,其實底層是調(diào)用的_element.markNeedsBuild() 函數(shù),這樣我們明白了,其實最終控制頁面的還是Element,那么Provider 它也巧妙的封裝了自己的_delegateState,是私有的,并沒有給我們公開使用,也沒有提供類似setState,但可以通過markNeedsNotifyDependents函數(shù)達(dá)到了和setState一樣的調(diào)用效果,一樣的都是讓所有子Widget進(jìn)行重建,可我們要的局部刷新呢?是在Consumer里?,來吧,不要走開,沒有廣告,精彩繼續(xù),接下來研究Consumer源碼
Consumer
class Consumer<T> extends SingleChildStatelessWidget {
/// 構(gòu)造函數(shù),必傳builder
Consumer({
Key key,
@required this.builder,
Widget child,
}) : assert(builder != null),
super(key: key, child: child);
/// 根據(jù) [Provider<T>] 提供的value,構(gòu)建的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,
);
}
}
- 這里源碼稍微有一點繞,Widget child傳給了父類SingleChildStatelessWidget,最終通過buildWithChild函數(shù)的參數(shù)child傳遞回來,而builder函數(shù)有收到了此child,然后再組合child和需要刷新的widget組合一個新的widget給Consumer。一句話就是說Consumer的構(gòu)造函數(shù)可以傳兩個widget一個是builder,一個是child,最終是通過builder構(gòu)建最終的widget,如果child不為空,那么你需要自己組織child和builder中返回widget的關(guān)系。
- Provider.of<T>(context) 獲取了共享數(shù)據(jù)value
Provider.of<T>(context) 是如何獲取數(shù)據(jù)的呢?繼續(xù)看源碼
/// 調(diào)用_inheritedElementOf函數(shù)
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;
}
- 通過 visitAncestorElements 往父級查找_InheritedProviderScope的實現(xiàn)類也就是InheritedWidget,當(dāng)找到是就返回_InheritedProviderScopeElement,而_InheritedProviderScopeElement正好可以拿到value,這個value也就是 _delegateState的value
@override
T get value => _delegateState.value;
走到這其實只是實現(xiàn)了讀取數(shù)據(jù),那么數(shù)據(jù)到底是如何刷新的呢?我們回過頭來看下面幾段代碼
- Model數(shù)據(jù)調(diào)用ChangeNotifier提供的函數(shù)notifyListeners
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,
);
},
));
}
}
}
}
這個時候遍歷所有的監(jiān)聽,然后執(zhí)行函數(shù)listener(),這里其實等于執(zhí)行VoidCallback的實例,那這個listener到底是哪個函數(shù)?
- 在ChangeNotifierProvider父類ListenableProvider的靜態(tài)函數(shù)中,自動訂閱了為觀察者
前面說了觀察者就是個普通函數(shù),而e.markNeedsNotifyDependents就是InheritedContext的一個函數(shù),當(dāng)你notifyListeners的時候執(zhí)行的就是它markNeedsNotifyDependents,上面我們知道m(xù)arkNeedsNotifyDependents類似setState效果,就這樣才實現(xiàn)了UI的刷新。
/// ListenableProvider 的靜態(tài)函數(shù)
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)這句話我們才能監(jiān)聽到數(shù)據(jù)的對吧,而且刷新的內(nèi)容也只是這一部分,我們再看下它的實現(xiàn)發(fā)現(xiàn)了另一個細(xì)節(jié)
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;
}
它調(diào)用了BuildContext的dependOnInheritedElement函數(shù),這個函數(shù)做了啥?
@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>();
觸發(fā)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(); /// 更新方法
}
}
所以說整體流程是這樣當(dāng)notifyListeners的時候其實是觸發(fā)了InheritedWidget的performRebuild,再到 build ,build后觸發(fā) notifyClients,notifyClients觸發(fā)notifyDependent,notifyDependent這個時候通過getDependencies獲取緩存好的Element,最終確定是否需要刷新然后調(diào)用dependent.didChangeDependencies();更新,哈哈,終于明白了,只要widget中通過Provider.of函數(shù)訂閱后,就會被InheritedWidget緩存在一個Map中,然后刷新頁面的時候,如果子Widget不在緩存的Map中,根本不會走刷新,而且如果shouldNotify變量是false也不會刷新,這個控制肯定是雖然子Widget訂閱了,但它自己就是不刷新,可以更加細(xì)粒度的控制。
源碼分析總結(jié)
至此明白
- Provider 通過緩存 inheritedElement 實現(xiàn)局部刷新
- 通過控制自己實現(xiàn)的Element 層來 更新UI
- 通過Element提供的unmount函數(shù)回調(diào)dispose,實現(xiàn)選擇性釋放
厲害嗎?還不錯哦。
冰山一角
其實我們明白了它的核心原理之后,剩下的就是擴(kuò)展該框架了,我目前只分析了ChangeNotifierProvider、Consumer,其實它還有很多很多,來一張圖嚇嚇你

圖片很大,請看原圖哦
看到這個圖,是不是覺得冰山一角呢?哈哈,不過還好,核心原理就是在InheritedProvider里面,我已經(jīng)帶你趟了一遍,剩下的就靠你自己了,加油。
結(jié)語
大家還有沒有喜歡的Flutter狀態(tài)管理框架,如果你想看到更多的狀態(tài)管理框架源碼分析,請你關(guān)注我哦,如果你讀到最后,如果你覺得還不錯,也請你點個贊,感謝??
