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

回顧

Flutter State Management狀態管理全面分析
上期咱們對Flutter的狀態管理有了全局的認知,也知道了如何分辨是非好壞,不知道也不要緊哦,咱們接下來還會更加詳細的分析,經過閱讀Provider源碼,來看看框架到底如何組織的,是如何給咱們提供便利的。設計模式

本期內容

經過官方咱們已經知道其實Provider就是對InheritedWidget的包裝,只是讓InheritedWidget用起來更加簡單且高可複用。咱們也知道它有一些缺點,如緩存

  • 容易形成沒必要要的刷新
  • 不支持跨頁面(route)的狀態,意思是跨樹,若是不在一個樹中,咱們沒法獲取
  • 數據是不可變的,必須結合StatefulWidget、ChangeNotifier或者Steam使用

我特別想弄明白,這些缺點在Provider的設計中是如何規避的,還有一個是Stream不會主動的close掉流的通道,不得不結合StatefulWidget使用,而Provider提供了dispose回調,你能夠在該函數中主動關閉,好厲害,如何作到的呢?帶着這些疑問,咱們去尋找答案網絡

如何使用

咱們先來使用它,而後在根據用例分析源碼,找到咱們想要的答案,先看一個簡單的例子app

step 1

第一步定義一個ChangeNotifier,來負責數據的變化通知框架

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

}

step 2

第二步,用ChangeNotifierProvider來訂閱Counter,不難猜出,ChangeNotifierProvider確定是InheritedWidget的包裝類,負責將Counter的狀態共享給子Widget,我這裏將ChangeNotifierProvider放到了Main函數中,並在整個Widget樹的頂端,固然這裏是個簡單的例子,我這麼寫問題不大,但你要考慮,若是是特別局部的狀態,請將ChangeNotifierProvider放到局部的地方而不是全局,但願你能明白個人用意less

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

第三步,接收數據經過Consumer<Counter>,Consumer是個消費者,它負責消費ChangeNotifierProvider生產的數據ide

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的缺點它規避了嗎?函數

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

咱們多加兩個子WIdget進去,排在Consumer的後面,OtherWidget什麼都不幹,不去訂閱Counter,OtherWidget2經過context.watch<Counter>().count函數監聽而不是Consumer,來看下效果同樣不,而後在build函數中都加入了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日誌
點擊刷新後
分析結論以下:post

  • Consumer、context.watch均可以監聽Counter變化
  • Consumer只會刷新本身
  • context.watch所在子widget不論是否是const都被重建後刷新數據
  • OtherWidget並無被重建,由於它沒有訂閱Counter

局部刷新確實實現了但要經過Consumer,第二個問題不支持跨頁面(route)的狀態,這個能夠肯定的說不支持,第三個問題數據是不可變的(只讀),通過這個例子能夠分辨出數據確實是可變的對吧,那麼數據是如何變化的呢?留個懸念,下面分析源碼中來看本質。

固然要想更完整的理解ChangeNotifier、ChangeNotifierProvider、Consumer的關係
請看圖
設計模式真是無處不在哈,ChangeNotifier與ChangeNotifierProvider實現了觀察者模式,ChangeNotifierProvider與Consumer又實現了生產者消費者模式,這裏不具體聊這倆個模式,若是還不瞭解,請你自行搜索學習哦。下面直接源碼分析

源碼分析

ChangeNotifier

在包package:meta/meta.dart下,是flutter sdk的代碼,並不屬於Provider框架的一部分哦,經過下方代碼能夠看出,這是一個標準的觀察者模型,而真正的監聽者就是typedef VoidCallback = void Function(); 是dart.ui包下定義的一個函數,沒人任何返回參數的函數。ChangerNotifier實現自抽象類Listenable,經過源碼的註釋咱們看到Listenable是一個專門負責維護監聽列表的一個抽象類。

ChangeNotifierProvider

class ChangeNotifierProvider<T extends ChangeNotifier>
    extends ListenableProvider<T> {
  static void _dispose(BuildContext context, ChangeNotifier notifier) {
    notifier?.dispose();
  }

  /// 使用`create`建立一個[ChangeNotifier]
  /// 當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,
        );
}

分析下構造

  • Create<T> create

是個通用函數typedef Create<T> = T Function(BuildContext context)用於建立T類,這裏負責建立ChangeNotifier

  • bool lazy

是否懶加載

  • TransitionBuilder builder

當builder存在時將不會用child作爲子Widget,追蹤到源碼實現能夠看到以下圖

  • Widget child

builder不存在時就用child

繼承自ListenableProvider<T>,來繼續分析它的源碼

class ListenableProvider<T extends Listenable> extends InheritedProvider<T> {
  ///  使用 [create] 建立一個 [Listenable] 訂閱它
  /// [dispose] 能夠選擇性的釋放資源當 [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 上面已經分析,它是負責管理觀察者列表的抽象
  • 它比子類ChangeNotifierProvider多了一個構造參數dispose,這個函數是typedef Dispose<T> = void Function(BuildContext context, T value); 是個回調,應該是當頁面被銷燬時觸發(等再深刻了源碼才能認證,目前只是猜想,咱們繼續看)

又繼承自InheritedProvider<T> ,別放棄,來跟我一塊兒往下看

class InheritedProvider<T> extends SingleChildStatelessWidget {
  /// 建立數據value並共享給子Widget
  /// 當 [InheritedProvider] 從樹中被釋放時,將自動釋放數據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);

  /// 暴漏給子孫一個已存在的數據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,
    );
  }
}

構造中多出來的參數

  • T update(BuildContext context, T value) 該函數返回數據變動值value,具體實如今_CreateInheritedProvider類中,說白了InheritedProvider<T>是個無狀態組件對嗎?那麼它要變動狀態確定要依賴於別人,而它建立出一個_CreateInheritedProvider類,_CreateInheritedProvider是_Delegate的實現類,_Delegate就是一個狀態的代理類,來看下_Delegate具體實現
@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的關係,一樣的_DelegateState提供了相似生命週期的函數,如willUpdateDelegate更新新的委託,dispose註銷等

  • UpdateShouldNotify<T> updateShouldNotify,
    void Function(T value) debugCheckInvalidValueType,
    StartListening<T> startListening,
    Dispose<T> dispose, 這些函數所有交給了委託類
  • 最關鍵的實現來了,到目前位置還沒看到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);
  }
}

至此你有沒有發現一個特色,全部的函數都被_Delegate帶走了,剩下的只有Widget交給了_InheritedProviderScope,這裏設計的也很好,畢竟InheritedWidget其實也就只能作到數據共享,跟函數並無什麼關係對吧。惟一有關係的地方,我猜想就是在InheritedWidget提供的Widget中調用

一個細節 owner: this 在 buildWithChild函數中,將InheritedProvider自己傳遞給InheritedWidget,應該是爲了方便調用它的_Delegate委託類,確定是用來回調各類函數。

... 快一點了,睡了,明天再更

繼續分享,_InheritedProviderScope惟一特殊的地方,咱們發現它本身建立了一個Element實現經過覆蓋createElement函數,返回_InheritedProviderScopeElement實例,flutter三板斧 Widget、Element、RenderObject,該框架本身實現一層Element,咱們都知道Widget是配置文件只有build和rebuild以及remove from the tree,而Element做爲一層虛擬Dom,主要負責優化,優化頁面刷新的邏輯,那咱們來詳細的分析一下_InheritedProviderScopeElement,看它都作了什麼?

/// 繼承自InheritedElement,由於InheritedWidget對應的Element就是它
/// 實現 InheritedContext,InheritedContext繼承自BuildContext,多了個T範型
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {

/// 構造函數,將Element對應的widget傳進來
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);
/// 是否須要通知依賴的Element變動
  bool _shouldNotifyDependents = false;
/// 是否容許通知變動
  bool _isNotifyDependentsEnabled = true;
/// 第一次構建
  bool _firstBuild = true;
/// 是否更新newWidget的Delegate委託
  bool _updatedShouldNotify = false;
/// 這個變量就是控制的數據變動,在Widget變動和Element依賴變動的時候都會被設置爲true
  bool _isBuildFromExternalSources = false;
/// 委託類的狀態(咱們猜想對了, 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函數調用了兩個函數updated(已更新完成),rebuild函數觸發從新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觸發真正調用rebuild以後被調用
  • updateDependencies、notifyDependent處理Element依賴邏輯
  • update、updated處理的widget更新邏輯
  • didChangeDependencies當此State對象的依賴項更改時調用,子類不多重寫此方法,由於框架老是在依賴項更改後調用build。一些子類確實重寫了此方法,由於當它們的依存關係發生變化時,它們須要作一些昂貴的工做(例如,網絡獲取),而且對於每一個構建而言,這些工做將太昂貴。
  • build() 構建須要的widget,Element在調用build的時候也會觸發Widget的build
  • void unmount() 這裏看到了_delegateState.dispose();的調用,如今找到了吧,當Element從樹中移除的時候,回掉了dispose函數。

來看一個生命週期的圖,輔助你理解源碼的調用關係

此圖引自大佬Reactive,他記錄了很詳細的生命週期圖,感謝做者的貢獻

  • notifyClients 這個函數幹嗎的?它是InheritedElement中實現的函數,經過官方文檔瞭解到,它是經過調用Element.didChangeDependencies通知全部從屬Element此繼承的widget已更改,此方法只能在構建階段調用,一般,在重建inherited widget時會自動調用此方法,還有就是InheritedNotifier,它是InheritedWidget的子類,在其Listenable發送通知時也調用此方法。
  • markNeedsNotifyDependents 若是你調用它,會強制build後 通知因此依賴Element刷新widget,看下面代碼,發現該函數在InheritedContext中定義,因此咱們能夠經過InheritedContext上下文來強制頁面的構建
abstract class InheritedContext<T> extends BuildContext {
  ///  [InheritedProvider] 當前共享的數據
  /// 此屬性是延遲加載的,第一次讀取它可能會觸發一些反作用,
  T get value;

  /// 將[InheritedProvider]標記爲須要更新依賴項
  /// 繞過[InheritedWidget.updateShouldNotify]並將強制rebuild
  void markNeedsNotifyDependents();

  /// setState是否至少被調用過一次
  /// [DeferredStartListening]可使用它來區分
  /// 第一次監聽,在「 controller」更改後進行重建。
  bool get hasValue;
}

小結一下
咱們先回顧一下咱們是如何使用InheritedWidget的,爲了能讓InheritedWidget的子Widget可以刷新,咱們不得不依賴於Statefulwidget,並經過State控制刷新Element,調用setState刷新頁面,其實底層是調用的_element.markNeedsBuild() 函數,這樣咱們明白了,其實最終控制頁面的仍是Element,那麼Provider 它也巧妙的封裝了本身的_delegateState,是私有的,並無給咱們公開使用,也沒有提供相似setState,但能夠經過markNeedsNotifyDependents函數達到了和setState同樣的調用效果,同樣的都是讓全部子Widget進行重建,可咱們要的局部刷新呢?是在Consumer裏?,來吧,不要走開,沒有廣告,精彩繼續,接下來研究Consumer源碼

Consumer

class Consumer<T> extends SingleChildStatelessWidget {
 
/// 構造函數,必傳builder
  Consumer({
    Key key,
    @required this.builder,
    Widget child,
  })  : assert(builder != null),
        super(key: key, child: child);

  /// 根據 [Provider<T>] 提供的value,構建的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函數的參數child傳遞回來,而builder函數有收到了此child,而後再組合child和須要刷新的widget組合一個新的widget給Consumer。一句話就是說Consumer的構造函數能夠傳兩個widget一個是builder,一個是child,最終是經過builder構建最終的widget,若是child不爲空,那麼你須要本身組織child和builder中返回widget的關係。
  • Provider.of<T>(context) 獲取了共享數據value

Provider.of<T>(context) 是如何獲取數據的呢?繼續看源碼

/// 調用_inheritedElementOf函數
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的實現類也就是InheritedWidget,當找到是就返回_InheritedProviderScopeElement,而_InheritedProviderScopeElement正好能夠拿到value,這個value也就是 _delegateState的value
@override
  T get value => _delegateState.value;

走到這其實只是實現了讀取數據,那麼數據究竟是如何刷新的呢?咱們回過頭來看下面幾段代碼

  1. Model數據調用ChangeNotifier提供的函數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,
              );
            },
          ));
        }
      }
    }
  }

這個時候遍歷全部的監聽,而後執行函數listener(),這裏其實等於執行VoidCallback的實例,那這個listener究竟是哪一個函數?

  1. 在ChangeNotifierProvider父類ListenableProvider的靜態函數中,自動訂閱了爲觀察者

前面說了觀察者就是個普通函數,而e.markNeedsNotifyDependents就是InheritedContext的一個函數,當你notifyListeners的時候執行的就是它markNeedsNotifyDependents,上面咱們知道markNeedsNotifyDependents相似setState效果,就這樣才實現了UI的刷新。

/// ListenableProvider 的靜態函數
 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)這句話咱們才能監聽到數據的對吧,並且刷新的內容也只是這一部分,咱們再看下它的實現發現了另外一個細節

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;
  }

它調用了BuildContext的dependOnInheritedElement函數,這個函數作了啥?

@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>();

觸發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();  /// 更新方法
    }
  }

因此說總體流程是這樣當notifyListeners的時候實際上是觸發了InheritedWidget的performRebuild,再到 build ,build後觸發 notifyClients,notifyClients觸發notifyDependent,notifyDependent這個時候經過getDependencies獲取緩存好的Element,最終肯定是否須要刷新而後調用dependent.didChangeDependencies();更新,哈哈,終於明白了,只要widget中經過Provider.of函數訂閱後,就會被InheritedWidget緩存在一個Map中,而後刷新頁面的時候,若是子Widget不在緩存的Map中,根本不會走刷新,並且若是shouldNotify變量是false也不會刷新,這個控制確定是雖然子Widget訂閱了,但它本身就是不刷新,能夠更加細粒度的控制。

源碼分析總結

至此明白

  • Provider 經過緩存 inheritedElement 實現局部刷新
  • 經過控制本身實現的Element 層來 更新UI
  • 經過Element提供的unmount函數回調dispose,實現選擇性釋放

厲害嗎?還不錯哦。

冰山一角

其實咱們明白了它的核心原理以後,剩下的就是擴展該框架了,我目前只分析了ChangeNotifierProvider、Consumer,其實它還有不少不少,來一張圖嚇嚇你

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

圖片很大,請看原圖哦
看到這個圖,是否是以爲冰山一角呢?哈哈,不過還好,核心原理就是在InheritedProvider裏面,我已經帶你趟了一遍,剩下的就靠你本身了,加油。

結語

你們還有沒有喜歡的Flutter狀態管理框架,若是你想看到更多的狀態管理框架源碼分析,請你關注我哦,若是你讀到最後,若是你以爲還不錯,也請你點個贊,感謝🙏

相關文章
相關標籤/搜索