Provider的局部刷新机制
以下以Provider 4.0.0版本进行分析。
使用方法就不说了,简单的来说,提供一个数据类型派生自ChangeNotifier,修改数据后调用notifyListeners()进行刷新通知。
有数据刷新需求的Widget外层包裹一个ListenableProvider,构造方法'create'将派生自ChangeNotifier的数据提供出去,'child'就用户自己写的Widget。
通过Provider.of<T>(context)获得数据,进行UI绘制或者修改数据后刷新。最关键的代码就是这一行。
//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;
}
⑴:
研究过ListenableProvider的源码可以知道,ListenableProvider build的Widget是_InheritedProviderScope<T>派生自InheritedWidget,提供的element是_InheritedProviderScopeElement<T>.
⑵:
了解Widget的构建后,可以知道这里的context就是调用Provider.of<T>()本身的BuildContext,即一般来说是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代表调用Provider.of<T>的这个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;
}
于是所有调用Provider.of<T>()的widget的Element都被存到了InheritedElement的_dependents.keys中。
⑶:
//_InheritedProviderScopeElement
//这里的value就是给到的数据
T get value => _delegateState.value;
//_CreateInheritedProviderState
T get value{
...
//这个startListening是在ListenableProvider中定义的闭包
_removeListener ??=delegate.startListening?.call(element, _value);
...
}
//ListenableProvider
static VoidCallback _startListening(InheritedContext<Listenable> e, Listenable value){
//这里的value就是提供的数据,e就是_InheritedProviderScopeElemet
value? .addListener(e.makeNeedsNotifyDependents);
return () => value?.removeListener(e.makNeedsNotifyDependents);
}
class ChangeNotifier implements Listenable{
ObserverList <VoidCallback> _listeners = ObserverList<VoidCallback>();
}
经过上面的第3步,将e.makeNeedsNotifyDependents这个闭包放入了_listeners,从上面的代码也可以看到,只有第一个闭包才会被放入。
现在准备工作都做好了,开始更新数据吧
//ChangeNotifier
void notifyListeners(){
final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
for(final VoidCallback listener in localListeners){
if(_listeners.contains())
listener();
}
}
于是,随即调用makeNeedsNotifyDependents()@_InheritedProviderScopeElemet
//_InheritedProviderScopeElemet
void makeNeedsNotifyDependents(){
markNeedsBuild();
_shouldNotifyDependents = true;
}
void markNeedsBuild(){
_dirty = true;
owner.scheduleBuildFor(this);
}
在下一个vsync来到时,因为该element被设置为_dirty,因为会进行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();
}
因此,每一次渲染,都会把调用个过Provider.of<T>()的Element保存起来,在下一帧到来的时候进行重新绘制渲染。
Provider提供了一个Selector,可以自定义是否进行rebuild,需要注意的是,如果其父节点进行了build,其必定rebuild,因为使用Selector的时候要特别注意其挂载的节点,否则就丧失了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;
}
}
其实同理,就可以编写自己的带cache的Widget了