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

回顧

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

本期內容

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

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

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

如何使用

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

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放到局部的地方而不是全局,希望你能明白我的用意

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生產的數據

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日志

點擊刷新后

分析結論如下:

  • 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狀態管理框架,如果你想看到更多的狀態管理框架源碼分析,請你關注我哦,如果你讀到最后,如果你覺得還不錯,也請你點個贊,感謝??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯系作者。