Flutter Provider 迄今為止最深、最全、最新的源碼分析

回顧

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ī)避了嗎?

  1. 容易造成不必要的刷新(解決了嗎?)

我們多加兩個子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ù)到底是如何刷新的呢?我們回過頭來看下面幾段代碼

  1. 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ù)?

  1. 在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,其實它還有很多很多,來一張圖嚇嚇你

http://jetpack.net.cn/provider.jpg

圖片很大,請看原圖哦
看到這個圖,是不是覺得冰山一角呢?哈哈,不過還好,核心原理就是在InheritedProvider里面,我已經(jīng)帶你趟了一遍,剩下的就靠你自己了,加油。

結(jié)語

大家還有沒有喜歡的Flutter狀態(tài)管理框架,如果你想看到更多的狀態(tài)管理框架源碼分析,請你關(guān)注我哦,如果你讀到最后,如果你覺得還不錯,也請你點個贊,感謝??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。

友情鏈接更多精彩內(nèi)容