Flutter Provider源码主流程分析

1、概述InheritedWidget原理;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流程会更清晰。
最后如果有不妥之处,请多多评论交流;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值