Flutter Provider-状态管理源码解析

2022-04-15 16:23:22 浏览数 (1)

这是一篇Provider的源码分析。我们从一个简单的例子开始分析,看provider怎么实现的状态管理。话不多说进入主题吧。

代码语言:javascript复制
class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

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

首先我们创建自己的继承于ChangeNotifier的状态类CounterChangeNotifier实现Listenable的方法。监听者通过addListener注册监听,当我们调用notifyListeners会触发监听者的方法回调。

接着我们创建ChangeNotifierProvider,实现对Counter状态监听。

代码语言:javascript复制
ChangeNotifierProvider(
  create: (context) => Counter(),
  builder: (context, child) {
    return Consumer<Counter>(
      builder: (context, counter, child) {
        return Text('${counter.count}');
      },
    );
  },
)

ChangeNotifierProvider继承自ListenableProvider,有两个构造方法,一个是默认的构造方法,一个是.value构造方法。通过create创作的会传递静态_dispose方法即自动管理create和dispose。.value传递一个已有的ChangeNotifier对象。的需要自己管理ChangeNotifierProvider的释放。(ps: 这里也是很多同学遇到value构造的ChangeNotifier不会随生命周期调用dispose问题)

代码语言:javascript复制
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();
  }
}

接着往上走 ListenableProvider 继承自InheritedProvider,这里同样是一个create和value的构造方法,向父类传递一个开始监听的方法_startListening_startListening实际就是ChangeNotifier监听刷新的实现,我们记住这个方法,后面会用到。

代码语言:javascript复制
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
  ListenableProvider({
    Key? key,
    required Create<T> create,
    Dispose<T>? dispose,
    bool? lazy,
    TransitionBuilder? builder,
    Widget? child,
  }) : super(
          key: key,
          startListening: _startListening,
          create: create,
          dispose: dispose,
          lazy: lazy,
          builder: builder,
          child: child,
        );

  /// Provides an existing [Listenable].
  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);
  }
}

接着往上走 InheritedProvider 继承自SingleChildStatelessWidgetSingleChildStatelessWidget是Provider为了方便编码而导入的库,可以简化开发。可以见库介绍。我们在这里关注buildWithChild方法即可。

InheritedProvider同样有create和value的构造方法。在构造方法创建不同的_Delegate_Delegate<T>是类似与Stateful Widget的一个类,createState可以创建对应的state对象。这里有两种类型的Delegate,_CreateInheritedProvider_ValueInheritedProvider对应create和value的构造方法。在这里我们关注_CreateInheritedProvider的实现。创建_CreateInheritedProvider_Delegate会传递create,startListening, dispose等方法。接下来就是实现Provider共享数据的关键,也就是InheriedWidget出场的时候啦。buildWithChild创建了_InheritedProviderScope并且将InheritedProvider作为owner参数传递过去。我们切换看下_InheritedProviderScope实现。

代码语言:javascript复制
// 代码有部分省略
class InheritedProvider<T> extends SingleChildStatelessWidget {
  InheritedProvider({
    Key? key,
    Create<T>? create,
    T Function(BuildContext context, T? value)? update,
    UpdateShouldNotify<T>? updateShouldNotify,
    void Function(T value)? debugCheckInvalidValueType,
    StartListening<T>? startListening,
    Dispose<T>? dispose,
    this.builder,
    bool? lazy,
    Widget? child,
  })  : _lazy = lazy,
        _delegate = _CreateInheritedProvider(
          create: create,
          update: update,
          updateShouldNotify: updateShouldNotify,
          debugCheckInvalidValueType: debugCheckInvalidValueType,
          startListening: startListening,
          dispose: dispose,
        ),
        super(key: key, child: child);

  /// Expose to its descendants an existing value,
  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);

  final _Delegate<T> _delegate;
  final bool? _lazy;

  @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!,
    );
  }
}

_InheritedProviderScope是一个InheritedWidget,将上面传递过来的InheritedProvider(owner)对象作为我们的inherited对象。我们知道updateShouldNotify是控制InheritedWidget是否刷新的。这里updateShouldNotify always return false是因为_InheritedProviderScope重写了InheritedWidgetcreateElement。由element自行管理notifyClients.

代码语言:javascript复制
class _InheritedProviderScope<T> extends InheritedWidget {
  const _InheritedProviderScope({
    required this.owner,
    required this.debugType,
    required Widget child,
  })  : assert(null is T),
        super(child: child);

  final InheritedProvider<T> owner;
  final String debugType;

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return false;
  }

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

_InheritedProviderScopeElement继承自InheritedElement,在performRebuild中,我们初始化了_delegateState并传递element给_Delegate。用于将InheritedElement标记脏。在下一帧build方法调用notifyClients(widget)通知刷新。

代码语言:javascript复制
// 代码有部分省略
class _InheritedProviderScopeElement<T> extends InheritedElement
    implements InheritedContext<T> {
  _InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
      : super(widget);

  static int _nextProviderId = 0;

  bool _shouldNotifyDependents = false;
  bool _debugInheritLocked = false;
  bool _isNotifyDependentsEnabled = true;
  bool _firstBuild = true;
  bool _updatedShouldNotify = false;
  bool _isBuildFromExternalSources = false;
  late _DelegateState<T, _Delegate<T>> _delegateState;
  late String _debugId;

  @override
  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _delegateState = widget.owner._delegate.createState()..element = this;
    }
    super.performRebuild();
  }

  @override
  Widget build() {
    if (widget.owner._lazy == false) {
      value; // this will force the value to be computed.
    }
    _delegateState.build(
      isBuildFromExternalSources: _isBuildFromExternalSources,
    );
    _isBuildFromExternalSources = false;
    if (_shouldNotifyDependents) {
      _shouldNotifyDependents = false;
      notifyClients(widget);
    }
    return super.build();
  }

  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) {
      return;
    }

    markNeedsBuild();
    _shouldNotifyDependents = true;
  }

  @override
  void unmount() {
    _delegateState.dispose();
    ...
  }

  @override
  T get value => _delegateState.value;
}

在_Delegate _CreateInheritedProviderState中value为Counter状态类,当获取value时会判断是否初始化,调用我们上面传入的create初始化状态类。还记得我们上面不断传递的startListening吗?也是在获取value这里创建了对Counter(ChangeNotifier)的监听。_removeListener ??= delegate.startListening?.call(element!, _value as T)

代码语言:javascript复制
// 代码有部分省略
class _CreateInheritedProviderState<T>
    extends _DelegateState<T, _CreateInheritedProvider<T>> {
  VoidCallback? _removeListener;
  bool _didInitValue = false;
  T? _value;
  _CreateInheritedProvider<T>? _previousWidget;
  FlutterErrorDetails? _initError;

  @override
  T get value {
    ...
    if (!_didInitValue) {
      _didInitValue = true;
      if (delegate.create != null) {
        ...
        _value = delegate.create!(element!);
        ...
      }
      ...
    }

    element!._isNotifyDependentsEnabled = false;
    _removeListener ??= delegate.startListening?.call(element!, _value as T);
    element!._isNotifyDependentsEnabled = true;
    assert(delegate.startListening == null || _removeListener != null);
    return _value as T;
  }

  @override
  void dispose() {
    super.dispose();
    _removeListener?.call();
    if (_didInitValue) {
      delegate.dispose?.call(element!, _value as T);
    }
  }
}

上面可知,dispose由element的unmout驱动,那Value的调用又在哪呢?没错就是上面用到的Consumer组件。Consumer实际是对Provider使用的封装。buildWithChild使用了Provider.of(context),我们看下这个实现,通过_inheritedElementOf获取到_InheritedProviderScopeElement,_InheritedProviderScopeElement的Value指向Delegate的value。listen参数用于是否监听刷新,实际就是用dependOnInheritedWidgetOfExactType将组件加入InheritedWidget的监听中。

代码语言:javascript复制
// 代码有部分省略
class Provider<T> extends InheritedProvider<T> {
  static T of<T>(BuildContext context, {bool listen = true}) {
    ...

    final inheritedElement = _inheritedElementOf<T>(context);

    if (listen) {
      // bind context with the element
      // We have to use this method instead of dependOnInheritedElement, because
      // dependOnInheritedElement does not support relocating using GlobalKey
      // if no provider were found previously.
      context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
    }

    final value = inheritedElement?.value;

    ...

    return value as T;
  }

  static _InheritedProviderScopeElement<T?>? _inheritedElementOf<T>(
      BuildContext context,
      ) {
    ...
  }
}

至此整个Provider的刷新链路就整理完毕啦~。简单总结一下:Provider.of(context)通过_inheritedElementOf获取到element,element.value实现了Counter状态类的初始化、监听。当Counter调用notifyListeners,会触发element的rebuild,element会notifyClients通知使用了dependOnInheritedWidgetOfExactType的context刷新。

0 人点赞