Flutter Provider原理解析

609 阅读5分钟

一、简介

Provider 是用于状态管理的轻量级库,基于 InheritedWidget 和 ChangeNotifier 构建,可以帮助开发者轻松地管理应用程序的状态,并提高代码的可读性和可维护性。

二、ChangeNotifier

ChangeNotifier 主要有两个方法:addListener 注册监听,notifyListeners 触发监听回调。

class ChangeNotifier implements Listenable {
  int _count = 0;
  static final List<VoidCallback?> _emptyListeners = List<VoidCallback?>.filled(0, null);
  List<VoidCallback?> _listeners = _emptyListeners;
  /// 省略无关代码...
  @override
  void addListener(VoidCallback listener) {
    assert(ChangeNotifier.debugAssertNotDisposed(this));
    if (kFlutterMemoryAllocationsEnabled && !_creationDispatched) {
      MemoryAllocations.instance.dispatchObjectCreated(
        library: _flutterFoundationLibrary,
        className: '$ChangeNotifier',
        object: this,
      );
      _creationDispatched = true;
    }
    if (_count == _listeners.length) {
      if (_count == 0) {
        _listeners = List<VoidCallback?>.filled(1, null);
      } else {
        final List<VoidCallback?> newListeners =
            List<VoidCallback?>.filled(_listeners.length * 2, null);
        for (int i = 0; i < _count; i++) {
          newListeners[i] = _listeners[i];
        }
        _listeners = newListeners;
      }
    }
    _listeners[_count++] = listener;
  }
  /// 省略无关代码...
  void notifyListeners() {
    assert(ChangeNotifier.debugAssertNotDisposed(this));
    if (_count == 0) {
      return;
    }

    _notificationCallStackDepth++;

    final int end = _count;
    for (int i = 0; i < end; i++) {
      try {
        _listeners[i]?.call();
      } catch (exception, stack) {
        /// 省略无关代码...
      }
    }
    /// 省略无关代码...
  }
}

三、InheritedWidget

InheritedWidget 是跨组件获取数据的关键组件,flutter 中常用的 Theme 也是基于 InheritedWidget 实现的,下面通过 Theme 来分析 InheritedWidget 的原理。

在 flutter 中常常通过 Theme.of(context)来获取主题相关的数据,当主题数据更新时,调用 Theme.of(context)的子 widget 也会随之更新。

  /// Theme.of 源码
  static ThemeData of(BuildContext context) {
    final _InheritedTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedTheme>();
    final MaterialLocalizations? localizations = Localizations.of<MaterialLocalizations>(context, MaterialLocalizations);
    final ScriptCategory category = localizations?.scriptCategory ?? ScriptCategory.englishLike;
    final ThemeData theme = inheritedTheme?.theme.data ?? _kFallbackTheme;
    return ThemeData.localize(theme, theme.typography.geometryThemeFor(category));
  }

可以看到 Theme.of 调用了"context.dependOnInheritedWidgetOfExactType"这个方法获取 inheritedTheme,再调用 inheritedTheme.theme.data 拿到了数据。

tip: context 就是 element 的封装

/// dependOnInheritedWidgetOfExactType源码
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>(
    {Object? aspect}) {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement? ancestor =
      _inheritedWidgets == null ? null : _inheritedWidgets![T];
  if (ancestor != null) {
    return dependOnInheritedElement(ancestor, aspect: aspect) as T;
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}

/// dependOnInheritedElement 源码
InheritedWidget dependOnInheritedElement(InheritedElement ancestor,
    {Object? aspect}) {
  assert(ancestor != null);
  _dependencies ??= HashSet<InheritedElement>();
  _dependencies!.add(ancestor);
  ancestor.updateDependencies(this, aspect);
  return ancestor.widget as InheritedWidget;
}

  • dependOnInheritedWidgetOfExactType 从子 element 的_inheritedWidgets 中获取到 InheritedElement,key 为_InheritedTheme 的 runtimeType,接下来调用 dependOnInheritedElement 并返回对应的 InheritedWidget。

  • dependOnInheritedElement 作用是建立 InheritedElement 和当前 element 的依赖关系,后面介绍 Provider 源码时会详细介绍,这里先关注数据是如何跨组件传递的。

  • 可以找到_inheritedWidgets 是在_updateInheritance 方法中赋值的

@override
void _updateInheritance() {
  assert(_lifecycleState == _ElementLifecycle.active);
  final PersistentHashMap<Type, InheritedElement> incomingWidgets =
      _parent?._inheritedWidgets ?? const PersistentHashMap<Type, InheritedElement>.empty();
  _inheritedWidgets = incomingWidgets.put(widget.runtimeType, this);
}

_updateInheritance 在 element 创建时调用,子 element 的_inheritedWidgets 来自父 element 的_inheritedWidgets。再来看_InheritedTheme 的祖父类 InheritedElement。

class InheritedElement extends ProxyElement {
  InheritedElement(InheritedWidget super.widget);

  final Map<Element, Object?> _dependents = HashMap<Element, Object?>();

  @override
  void _updateInheritance() {
    assert(_lifecycleState == _ElementLifecycle.active);
    final PersistentHashMap<Type, InheritedElement> incomingWidgets =
        _parent?._inheritedWidgets ?? const PersistentHashMap<Type, InheritedElement>.empty();
    _inheritedWidgets = incomingWidgets.put(widget.runtimeType, this);
  }
  /// 省略无关代码...
}
  • InheritedElement 重写_updateInheritance 把自身的 element 的引用存入_inheritedWidgets 中。
  • 后代 element 的__inheritedWidgets 来自父类,这样就能存有祖先 InheritedElement 的引用,通过 InheritedWidget.runtimeType 就能获取 InheritedElement,再通过 InheritedElement.widget.data 就可以拿到保存在 InheritedWidget 中的数据。

四、InheritedWidget 小结

  • InheritedElement 重写_updateInheritance 方法将自身引用存入_inheritedWidgets 中,key 为 InheritedWidget 的 runTimeType。
  • 后代 element 的_inheritedWidgets 来自父 element 的_inheritedWidgets,那么在后代 element 的_inheritedWidgets 中就会存有祖先 InheritedElement 的引用。
  • 子 widget 调用 context.dependOnInheritedWidgetOfExactType 方法,传入需要查找的 InheritedWidget runTimeType,从子 element 的_inheritedWidgets 拿到对应类型的最近祖先 InheritedElement。
  • 调用 InheritedElement.widget.value 拿到 InheritedWidget 中的数据。

InheritedWidget原理图.jpg

五、Provider 源码分析

1、ChangeNotifierProvider 源码

/// ChangeNotifierProvider源码
class ChangeNotifierProvider<T extends ChangeNotifier?>
    extends ListenableProvider<T> {
  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,
        );

  /// Provides an existing [ChangeNotifier].
  ChangeNotifierProvider.value({
    Key? key,
    required T value,
    TransitionBuilder? builder,
    Widget? child,
  }) : super.value(
          key: key,
          builder: builder,
          value: value,
          child: child,
        );

  static void _dispose(BuildContext context, ChangeNotifier? notifier) {
    notifier?.dispose();
  }
}

ChangeNotifierProvider 继承 ListenableProvider ,有两种构造函数,一个是传入数据的 create 方法,一个是直接传入数据 value。value 的类型或者 create 方法的返回值类型是继承 ChangeNotifier 的类 T。

ChangeNotifierProvider 的两种构造方法差别不大,因为篇幅原因,后续源码中只会展示直接传入 value 的构造方法

class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
  /// 省略ListenableProvider构造函数...
  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 e,
    Listenable? value,
  ) {
    value?.addListener(e.markNeedsNotifyDependents);
    return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}

ListenableProvider 继承 InheritedProvider,ListenableProvider 中有个重要的方法_startListening,后面我们可以看到它调用的地方。

class InheritedProvider<T> extends SingleChildStatelessWidget {

  /// 省略InheritedProvider构造函数代码...
  InheritedProvider.value({
    Key? key,
    required T value,
    UpdateShouldNotify<T>? updateShouldNotify,
    StartListening<T>? startListening,
    bool? lazy,
    this.builder,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _ValueInheritedProvider(
          value: value,
          updateShouldNotify: updateShouldNotify,
          startListening: startListening,
        ),
        super(key: key, child: child);

  /// 省略部分无关代码...

  @override
  _InheritedProviderElement<T> createElement() {
    return _InheritedProviderElement<T>(this);
  }

  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
    /// 省略部分代码
    return _InheritedProviderScope<T?>(
      owner: this,
      // ignore: no_runtimetype_tostring
      debugType: kDebugMode ? '$runtimeType' : '',
      child: builder != null
          ? Builder(
              builder: (context) => builder!(context, child),
            )
          : child!,
    );
  }
}

InheritedProvider 是一个 widget,持有代理类_delegate,在 buildWithChild 中返回了_InheritedProviderScope,并将 this 作为_InheritedProviderScope 的 owner 属性传入。

class _InheritedProviderScope<T> extends InheritedWidget {
   /// 省略部分无关代码

  @override
  _InheritedProviderScopeElement<T> createElement() {
    return _InheritedProviderScopeElement<T>(this);
  }
}

/// _InheritedProviderScopeElement源码
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
  /// 省略部分无关代码
  late final _DelegateState<T, _Delegate<T>> _delegateState =
      widget.owner._delegate.createState()..element = this;
  late String _debugId;

  /// 省略部分无关代码...

  @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;
        Future.microtask(() {
          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);

    if (kDebugMode) {
      ProviderBinding.debugInstance.providerDidChange(_debugId);
    }

    var shouldNotify = false;
    if (dependencies != null) {
      if (dependencies is _Dependency<T>) {
        if (dependent.dirty) {
          return;
        }

        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 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);
  }
  /// 省略部分无关代码...
}

InheritedProviderScopeElement 中有很多关键的逻辑,接下来结合 Provider 最常用的两个使用 context.read 和 content.select 的源码来分析 InheritedProviderScopeElement 实现了哪些关键逻辑。

context.read 仅获取数据不监听数据改变

context.select 获取数据同时监听数据改变刷新 widget

2、context.read 源码分析

/// context.read源码
T read<T>() {
  return Provider.of<T>(this, listen: false);
}

/// Provider.of源码
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;
}

context.read 调用 Provider.of, listen 传 false,Provider.of 中调用_inheritedElementOf 获取 inheritedElement,再调用 inheritedElement.value 获得数据。

/// _inheritedElementOf源码
static _InheritedProviderScopeElement<T?>? _inheritedElementOf<T>(
  BuildContext context,
) {
  /// 忽略无关代码...
  final inheritedElement = context.getElementForInheritedWidgetOfExactType<
      _InheritedProviderScope<T?>>() as _InheritedProviderScopeElement<T?>?;
  if (inheritedElement == null && null is! T) {
    throw ProviderNotFoundException(T, context.widget.runtimeType);
  }
  return inheritedElement;
}

/// getElementForInheritedWidgetOfExactType 源码
@override
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
  assert(_debugCheckStateIsActiveForAncestorLookup());
  final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
  return ancestor;
}

在_inheritedElementOf 方法中调用了 getElementForInheritedWidgetOfExactType 方法,这个和前面介绍的 dependOnInheritedWidgetOfExactType 都是从_inheritedWidgets 中获取 InheritedElement,只是少了调用了 dependOnInheritedElement 方法。

/// inheritedElement?.value
@override
T get value => _delegateState.value;

/// _delegateState的创建
late final _DelegateState<T, _Delegate<T>> _delegateState =
    widget.owner._delegate.createState()..element = this;

获取 inheritedElement 后,调用了_InheritedProviderScopeElement 的 get value 方法,_delegateState 由 owner._delegate.createState 创建。接着来看_delegate 的 createState 方法

_delegate = _ValueInheritedProvider(
  value: value,
  updateShouldNotify: updateShouldNotify,
  startListening: startListening,
),

_delegate = _CreateInheritedProvider(
  create: create,
  update: update,
  updateShouldNotify: updateShouldNotify,
  debugCheckInvalidValueType: debugCheckInvalidValueType,
  startListening: startListening,
  dispose: dispose,
)

InheritedProvider 两种构造方法,_delegate 有两种对应的实现方式,两种主要逻辑差别不大,下面我们主要看_ValueInheritedProvider。

class _ValueInheritedProvider<T> extends _Delegate<T> {
  /// 省略无关代码...
  final T value;
  /// 省略无关代码...
  @override
  _ValueInheritedProviderState<T> createState() {
    return _ValueInheritedProviderState<T>();
  }
}

class _ValueInheritedProviderState<T>
    extends _DelegateState<T, _ValueInheritedProvider<T>> {
  VoidCallback? _removeListener;

  @override
  T get value {
    element!._isNotifyDependentsEnabled = false;
    _removeListener ??= delegate.startListening?.call(element!, delegate.value);
    element!._isNotifyDependentsEnabled = true;
    assert(delegate.startListening == null || _removeListener != null);
    return delegate.value;
  }
  /// 省略无关代码...
}
  • 在_ValueInheritedProviderState 中,我们看到了 inheritedElement?.value 最终调用的方法 get value。
  • 在 get value 方法中调用了 _removeListener ??= delegate.startListening?.call(element!, _value as T);然后返回了 delegate.value。
  • 结合前面的 _startListening 函数,可以知道这里将_InheritedProviderScopeElement 的 markNeedsNotifyDependents 方法加入到了 value 对象的 _listeners 中,当 value 对象调用 notifyListener 方法,_InheritedProviderScopeElement 的 markNeedsNotifyDependents 方法就会调用。

2、context.select 源码分析

R select<T, R>(R Function(T value) selector) {
  /// 省略无关代码...
  final inheritedElement = Provider._inheritedElementOf<T>(this);
  try {
    final value = inheritedElement?.value;
    /// 省略无关代码...
    final selected = selector(value);
    if (inheritedElement != null) {
      dependOnInheritedElement(
        inheritedElement,
        aspect: (T? newValue) {
          if (newValue is! T) {
            throw ProviderNullException(T, widget.runtimeType);
          }
          return !const DeepCollectionEquality()
              .equals(selector(newValue), selected);
        },
      );
    } else {
      dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
    }
    return selected;
  } finally {
    /// 省略无关代码...
  }
}ß

在 select 中同样调用了_inheritedElementOf 和 inheritedElement?.value 来获取数据,不一样的 select 多了一步:调用 dependOnInheritedElement,并传入了 aspect 回调函数,aspect 的返回值是 newValue 和当前值是否相等。

//// dependOnInheritedElement源码
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
  assert(ancestor != null);
  _dependencies ??= HashSet<InheritedElement>();
  _dependencies!.add(ancestor);
  ancestor.updateDependencies(this, aspect);
  return ancestor.widget as InheritedWidget;
}

/// _InheritedProviderScopeElement updateDependencies源码
@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;
      Future.microtask(() {
        selectorDependency
          ..shouldClearMutationScheduled = false
          ..shouldClearSelectors = true;
      });
    }
    selectorDependency.selectors.add(aspect);
    setDependencies(dependent, selectorDependency);
  } else {
    // subscribes to everything
    setDependencies(dependent, const Object());
  }
}

/// setDependencies源码
@protected
void setDependencies(Element dependent, Object? value) {
  _dependents[dependent] = value;
}
  • dependOnInheritedElement 将 InheritedElement 的引用存入子 element 的_dependencies 中再调用 InheritedElement 的 updateDependencies 方法。
  • updateDependencies 将子 element 作为 key 存入_dependents 中,value 是 selectorDependency,selectorDependency.selectors 中存入 aspect。

3、markNeedsNotifyDependents

通过前面 context.read 和 context.select 的分析,知道了当数据 model 调用 notifyListener 更新时,会调用 InheritedElement 的 markNeedsNotifyDependents 方法。 markNeedsNotifyDependents 最终会调用到 notifyDependent

@override
void notifyDependent(InheritedWidgetoldWidget, Element dependent) {
  final dependencies = getDependencie(dependent);
  if (kDebugMode) {
    ProviderBinding.debugInstanceproviderDidChange(_debugId);
  }
  var shouldNotify = false;
  if (dependencies != null) {
    if (dependencies is _Dependency<T>) {
      if (dependent.dirty) {
        return;
      }
      for (final updateShouldNotify independencies.selectors) {
        try {
          assert(() {
            _debugIsSelecting = true;
            return true;
          }());
          shouldNotify = updateShouldNotif(value);
        } finally {
          assert(() {
            _debugIsSelecting = false;
            return true;
          }());
        }
        if (shouldNotify) {
          break;
        }
      }
    } else {
      shouldNotify = true;
    }
  }
  if (shouldNotify) {
    dependent.didChangeDependencies();
  }
}

在 notifyDependent 中会遍历_dependencies,_dependencies 中 key 为子 element,value.selectors 中保存了比较新旧值是否相等的回调函数,如果返回值相等就不调用 didChangeDependencies 触发子 element 的更新。

因为 context.read 没有将子 element 加入到 dependencies 中,所以子 element 不会随着数据更新

markNeedsNotifyDependents 触发更新的原理就不过多介绍了,感兴趣的可以看源码和其他博客

4、总结

  • ChangeNotifierProvider 的祖父类是 InheritedProvider,InheritedProvider buildWithChild 将 child widget 包裹一层_InheritedProviderScope
  • _InheritedProviderScope 继承 InheritedWidget,对应的 element: _InheritedProviderScopeElement 继承 InheritedElement,InheritedElement 会将自身的引用存入_inheritedWidgets 中。子 element 继承父 element 的_inheritedWidgets,从而可以获取到_InheritedProviderScopeElement
  • context.read 从子 element 的_inheritedWidgets 中获取到 InheritedProviderScopeElement,并调用 value 方法获取数据的同时,将 InheritedProviderScopeElement 的 markNeedsNotifyDependents 方法添加到 value 的监听回调中
  • context.select 比 context.read 多调了 dependOnInheritedElement 方法,会将子 element 的引用作为 key,比较新旧数据的方法作为 value,存入 InheritedProviderScopeElement 的_dependents 中。
  • value 数据更新后调用 notifyListener,会调用 InheritedProviderScopeElement 的 markNeedsNotifyDependents 方法,再调用 notifyDependent 方法,遍历_dependents,调用比值的回调函数,值不同就调用子 element 的 didChangeDependencies 更新子 widget。