以下以Provider 4.0.0版本進(jìn)行分析攒盈。
使用方法就不說(shuō)了渔工,簡(jiǎn)單的來(lái)說(shuō),提供一個(gè)數(shù)據(jù)類型派生自ChangeNotifier摔癣,修改數(shù)據(jù)后調(diào)用notifyListeners()進(jìn)行刷新通知姑曙。
有數(shù)據(jù)刷新需求的Widget外層包裹一個(gè)ListenableProvider襟交,構(gòu)造方法'create'將派生自ChangeNotifier的數(shù)據(jù)提供出去,'child'就用戶自己寫(xiě)的Widget渣磷。
通過(guò)Provider.of<T>(context)獲得數(shù)據(jù)婿着,進(jìn)行UI繪制或者修改數(shù)據(jù)后刷新。最關(guān)鍵的代碼就是這一行醋界。
//provider.dart
static T of<T>(BuildContext context, {bool listen = true}){
//⑴竟宋,inheritedElement是_InheritedProviderScopeElement
final inheritedElement = _inheritedElementOf<T>(context);
if(listener){
//⑵
context.dependOnInheritedElement(inheritedElement);
}
//⑶
return inheritedElement.value;
}
⑴:
研究過(guò)ListenableProvider的源碼可以知道,ListenableProvider build的Widget是_InheritedProviderScope<T>派生自InheritedWidget,提供的element是_InheritedProviderScopeElement<T>.
⑵:
了解Widget的構(gòu)建后形纺,可以知道這里的context就是調(diào)用Provider.of<T>()本身的BuildContext丘侠,即一般來(lái)說(shuō)是StatefulElement/Element,而StatefulElement派生自Element
//StatefulElement
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object aspect}){
//
return super.dependOnInheritedElement(ancestor, aspect:aspect);
}
//Element
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, {Object aspect}){
...
//this代表調(diào)用Provider.of<T>的這個(gè)Element逐样,
ancestor.updateDependencies(this, aspect);
}
//InheritedElement
void updateDependencies(Element dependent, Object aspect){
setDependencies(dependent, null);
}
final Map<Element, Object> _dependents = HashMap<Element, Object>();
void setDependencies(Element dependent, Object value){
_dependents[dependent] = value;
}
于是所有調(diào)用Provider.of<T>()的widget的Element都被存到了InheritedElement的_dependents.keys中蜗字。
⑶:
//_InheritedProviderScopeElement
//這里的value就是給到的數(shù)據(jù)
T get value => _delegateState.value;
//_CreateInheritedProviderState
T get value{
...
//這個(gè)startListening是在ListenableProvider中定義的閉包
_removeListener ??=delegate.startListening?.call(element, _value);
...
}
//ListenableProvider
static VoidCallback _startListening(InheritedContext<Listenable> e, Listenable value){
//這里的value就是提供的數(shù)據(jù),e就是_InheritedProviderScopeElemet
value? .addListener(e.makeNeedsNotifyDependents);
return () => value?.removeListener(e.makNeedsNotifyDependents);
}
class ChangeNotifier implements Listenable{
ObserverList <VoidCallback> _listeners = ObserverList<VoidCallback>();
}
經(jīng)過(guò)上面的第3步脂新,將e.makeNeedsNotifyDependents這個(gè)閉包放入了_listeners,從上面的代碼也可以看到挪捕,只有第一個(gè)閉包才會(huì)被放入。
現(xiàn)在準(zhǔn)備工作都做好了争便,開(kāi)始更新數(shù)據(jù)吧
//ChangeNotifier
void notifyListeners(){
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
for(final VoidCallback listener in localListeners){
if(_listeners.contains())
listener();
}
}
于是级零,隨即調(diào)用makeNeedsNotifyDependents()@_InheritedProviderScopeElemet
//_InheritedProviderScopeElemet
void makeNeedsNotifyDependents(){
markNeedsBuild();
_shouldNotifyDependents = true;
}
void markNeedsBuild(){
_dirty = true;
owner.scheduleBuildFor(this);
}
在下一個(gè)vsync來(lái)到時(shí),因?yàn)樵揺lement被設(shè)置為_(kāi)dirty滞乙,因?yàn)闀?huì)進(jìn)行build工作
Widget build(){
if(_shouldNotifyDependents){
_shouldNotifyDependents = false;
notifyClients(Widget);
}
return super.build();
}
//ProxyElement
Widget build() => widget.child;
void notifyClient(InheritedWidget oldWidget){
for(final Element dependent in _dependents.key){
notifyDependent(oldWidget, dependents);
}
}
void notifyDependent(covariant InheritedWidegt oldWidget, Element dependent){
dependent.didChangeDependencies();
}
void didChangeDependencies(){
makeNeedsBuild();
}
因此奏纪,每一次渲染鉴嗤,都會(huì)把調(diào)用個(gè)過(guò)Provider.of<T>()的Element保存起來(lái),在下一幀到來(lái)的時(shí)候進(jìn)行重新繪制渲染序调。
Provider提供了一個(gè)Selector醉锅,可以自定義是否進(jìn)行rebuild,需要注意的是发绢,如果其父節(jié)點(diǎn)進(jìn)行了build硬耍,其必定rebuild,因?yàn)槭褂肧elector的時(shí)候要特別注意其掛載的節(jié)點(diǎn)朴摊,否則就喪失了Selector提供的本意默垄。
class _Selector0State<T> extends SingleChildState<Selector0<T>> {
T value;
Widget cache;
Widget oldWidget;
@override
Widget buildWithChild(BuildContext context, Widget child) {
final selected = widget.selector(context);
var shouldInvalidateCache = oldWidget != widget ||
(widget._shouldRebuild != null &&
widget._shouldRebuild.call(value, selected)) ||
(widget._shouldRebuild == null &&
!const DeepCollectionEquality().equals(value, selected));
if (shouldInvalidateCache) {
value = selected;
oldWidget = widget;
cache = widget.builder(
context,
selected,
child,
);
}
return cache;
}
}
其實(shí)同理此虑,就可以編寫(xiě)自己的帶cache的Widget了