1、概述InheritedWidget原理; InheritedWidget:原理简单来讲,就是A作为InheritedWidget父节点持有需要局部更新的子节点集合_dependents,子节点也持有父节点A引用_inheritedWidgets;当需要局部刷新子节点时候,通知A去重建顺带遍历子节点集合_dependent;当子节点标记为脏节点时候,刷新就会重建所有的局部子节点,类似图中的B…等等,而未加入_depedndents中的其他子节点则不跟新;不了解的可以找相关文章。这也是Provider的基础所在。
2、Provider解析;
尽量极致简单,从一个简单场景入手解读:
class LoginModel extends ChangeNotifier{
bool _isLogin= false;
set isLogin(bool value) {
_isLogin = value;
notifyListeners();
}
}
LoginModel 继承一个被观察者(ChangeNotifier)持有多个观察者的引用,当调用isLogin,会执行notifyListeners去通知观察者执行代码;想想如果此处诱发通知A重建(类似执行setState效果),那么不就是完美的入口?
void notifyListeners() {
for (int i = 0; i < end; i++) {
...
try {
_listeners[i]?.call();
} catch (exception, stack) {
}
...
}
}
notifyListeners主要逻辑就是直接遍历观察者集合其实一般只有一个,执行观察者方法函数(_listeners是function类型集合),那观察者从哪里注册进去的呢?
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => LoginModel ()),
],
child: MyApp(),
))
从ChangeNotifierProvider继承ListenableProvider
ChangeNotifierProvider(***) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
builder: builder,
child: child,
);
ListenableProvider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);
static VoidCallback _startListening(
InheritedContext e,
Listenable? value,
) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
直接把InheritedContext的markNeedsNotifyDependents注册到被观察者上去;
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
markNeedsNotifyDependents里面直接调用了markNeedsBuild,将自身标记为脏节点,而这个自身就是父节点ChangeNotifierProvider,也就是上图中的A节点,markNeedsBuild里面又回去请求刷新等待垂直信号更新节点;
官方InheritedWidget对应InheritedElement,而provider的 _InheritedProviderScope对应_InheritedProviderScopeElement,只是后两者分别继承前者覆写了些方法,而_InheritedProviderScopeElement又实现了InheritedContext。
abstract class InheritedContext<T> extends BuildContext {
T get value;
void markNeedsNotifyDependents();
bool get hasValue;
}
_InheritedProviderScope<T> extends InheritedWidget
_InheritedProviderScopeElement<T> extends InheritedElement
3、接收垂直信号并完成刷新;
当信号来时会首先经过handleDrawFrame->_handlePersistentFrameCallback->drawFrame->buildScope ,buildScope执行所有收集的脏节点的刷新;
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
...省略
while (index < dirtyCount) {
final Element element = _dirtyElements[index];
element.rebuild();
}
...省略
}
此时会执行Element的rebuild,意味着执行_InheritedProviderScopeElement的rebuild,rebuild里面执行performRebuild。
void performRebuild(){
...省略
built = build();
_child = updateChild(_child, built, slot);
...省略
}
updateChild(){
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
}
}
由于作为根节点的_InheritedProviderScopeElement的child是传递进来的,所以每次child值不变,导致子树不更新,直接保持原样,这就是所谓的子节点不全部跟新;
那么需要更新的子节点B在哪里标记为脏节点的呢,在上面performRebuild中的build(),_InheritedProviderScopeElement覆写了build()方法;
Widget build() {
if (widget.owner._lazy == false) {
value; // this will force the value to be computed.
}
_delegateState.build(
isBuildFromExternalSources: _isBuildFromExternalSources,
);
_isBuildFromExternalSources = false;
if (_shouldNotifyDependents) {
_shouldNotifyDependents = false;
notifyClients(widget);
}
return super.build();
}
notifyClients遍历所有需要更新的子节点,并且标记为脏节点
void notifyClients(InheritedWidget oldWidget) {
for (final Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
_dependents.keys 就是所有需要更新的子节点,keys 它们又从哪里加入的呢?;
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
dependent.didChangeDependencies();
}
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
notifyDependent调用节点didChangeDependencies,didChangeDependencies又调用markNeedsBuild,markNeedsBuild则把自身标记为脏节点,并请求垂直信号刷新。
小结:到此为止局部子节点标记成脏节点调用完成;
4、那子节点怎么注册到父节点A里面去的呢?,解释上面的keys赋值
子节点:
Consumer<LoginModel>(
builder: (context, notifier, child) {
return Container(***);
}
}
class SingleChildStatelessWidget extends StatelessWidget
class Consumer<T> extends SingleChildStatelessWidget {
Widget buildWithChild(BuildContext context, Widget? child) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
}
SingleChildStatelessWidget 只是个无状态的StatelessWidget,当创建时候,会执行buildWithChild;Provider.of(context)的dependOnInheritedWidgetOfExactType又通过dependOnInheritedElement把当前的Consumer节点注册到图里面的A节点的_depedndents,并完成相互绑定;
static T of<T>(BuildContext context, {bool listen = true}) {
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {
context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
}
final value = inheritedElement?.value;
if (_isSoundMode) {
if (value is! T) {
throw ProviderNullException(T, context.widget.runtimeType);
}
return value;
}
return value as T;
}
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
if (ancestor != null) {
return dependOnInheritedElement(ancestor, aspect: aspect) as T;
}
_hadUnsatisfiedDependencies = true;
return null;
}
总结:继承ChangeNotifier的***Model(LoginModel),通过notifyListeners去通知Provider父节点去刷新,这个动作就好比setState(), Provider父节点在重建过程中由于自身子树不需要全部刷新,而只去刷新需要的Consumer节点,最终完成局部刷新的任务;看晕的话,多调试debug流程会更清晰。
最后如果有不妥之处,请多多评论交流;