Provider组件使用及源码解析

Chris Scoot
26 min readMay 3, 2024

version: 6.1.2

ChangeNotifierProvider

  • 父类:ListenableProvider,主要区别在于ListenableProvider监听的是Listenable模型,而ChangeNotifierProvider则监听ChangeNotifier模型(一种Listenable)。如果需要自定义Listenable或者监听其他Listenable例如Animation,则可以使用父类。
  • 功能:自己会监听一个ChangeNotifier模型,一旦模型改变后,会调用markNeedsNotifyDependents,通知所有依赖者rebuild

这是使用最多的组件,基于inheritedWidget实现。该组件携带一个数据模型,共享给下面的所有child widget 访问(read)、监听(watch)。

由此可知:

  • 携带的数据,必须继承ChangeNotifier,且数据中可以手动调用notifyListeners方法来通知Provider更新,从而间接更新所有依赖
  • 若一个child widget监听了(watch)ChangeNotifierProvider携带的数据变化,则数据改变后该widget会rebuild
  • 若一个child widget读取了(read)ChangeNotifierProvider携带的数据,获取的是当时的数据,且数据改变后不会通知该widget

ListenableProvider

  • 父类:InheritedProvider
  • 作用:与ChangeNotifierProvider一样,只不过他可以监听更为原始的Listenable类型的模型,通过源码可知,其底层原理是收到消息后,直接调用InheritedContext的markNeedsNotifyDependents方法通知所以依赖更新,而不是通过rebuild InheritedWidget来触发。

QA:下面两个构造函数:传create,和传value又何区别呢?

///作为父类,可以监听更为原始的Listenable类型数据变化
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,///当调用_startListening时就已经添加了监听,当调用返回值则取消监听
child: child,///作为优化使用
);

///全局函数
static VoidCallback _startListening(
InheritedContext<Listenable?> e,
Listenable? value,
) {
value?.addListener(e.markNeedsNotifyDependents);///监听方法就是通知InheritedWidget更新其所有依赖
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}

InheritedProvider

携带数据的Provider都是继承自此类

class InheritedProvider<T> extends SingleChildStatelessWidget {
///携带一个,从create方法返回的数据作为共享数据
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);

/// 携带一个已经存在的数据,Provider从数中移除时不会自动dispose模型
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);

InheritedProvider._constructor({
Key? key,
required _Delegate<T> delegate,
bool? lazy,
this.builder,
Widget? child,
}) : _lazy = lazy,
_delegate = delegate,
super(key: key, child: child);

final _Delegate<T> _delegate;///实际数据保存在此_ValueInheritedProvider or _CreateInheritedProvider
final bool? _lazy;///是否需要懒加载

/// For an explanation on the `child` parameter that `builder` receives,
/// see the "Performance optimizations" section of [AnimatedBuilder].
final TransitionBuilder? builder;

///一旦调用下面的方法,才能通知所有的依赖者rebuild,build函数中直接调用本函数
///QA:此函数会被动态调用吗?
@override
Widget buildWithChild(BuildContext context, Widget? child) {
///这是最为核心的代码
//当函数重新调用时,_InheritedProviderScope(InheritedWidget的子类)会重新创建,但是child参数不会。根据继承式组件原理,一旦InheritedWidget重新创建,所有依赖者depedents都会被标记为重建,从而更新所有依赖者
return _InheritedProviderScope<T?>(///InheritedWidget子类,这是数据能共享的本质
owner: this,//真正的数据保存在自己手里,而依赖者需要数据只能通过InheritedWidget来找,
debugType: kDebugMode ? '$runtimeType' : '',
child: builder != null
? Builder(
builder: (context) => builder!(context, child),
)
: child!,
);
}
}

Provider

父类:InheritedProvider

没有核心内容,基本上就是对父类的简单封装

该组件携带一个数据模型,共享给下面的所有child widget 访问(read),监听(watch)。当Provider在树中被移除,数据模型也会被释放。

由此可知:

1、与ChangeNotifierProvider的不同是,携带的数据不需要继承Listenable。因而数据无法通过调用notifyListeners方法触发Provider更新(Provider并不监听数据的变化)。

2、若一个child widget监听(watch)Provider携带的数据变化,是不会得到通知的。因为Provider自己也感知不到数据变化,它只是简单的持有数据。

3、若一个child widget读取(read)Provider携带的数据,获取的是即时数据,后续变化后是不会得到通知

4、持有的数据不能是: Listenable/Stream 的子类,否则DEBUG运行会报错,设置Provider.debugCheckInvalidValueType = null;可取消验证

QA:问题来了,既然Provider没有监听数据变化,那么一个child Widget调用watch方法监听有何意义呢?

答:当Provider自己rebuild的时候,其依赖者依然会被更新。例如,Provider A depend on另一个Provider B

///1、只向下共享数据,但数据模型无法主动触发Provider更新 【因为Provider根本就没有监听数据的变化】
///T 类型,不能是Lisenable\Stream等可监听类型,如果需要监听数据变化,使用ChangeNotifierProvider
class Provider<T> extends InheritedProvider<T> {
Provider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
lazy: lazy,
builder: builder,
create: create,
dispose: dispose,
debugCheckInvalidValueType: kReleaseMode
? null
: (T value) =>
Provider.debugCheckInvalidValueType?.call<T>(value),
child: child,
);


Provider.value({
Key? key,
required T value,
UpdateShouldNotify<T>? updateShouldNotify,
TransitionBuilder? builder,
Widget? child,
}) : assert(() {
///默认情况下,DEBUG模式,会检查value的类型是否是Listenabel,Stream,是则抛出异常
Provider.debugCheckInvalidValueType?.call<T>(value);
return true;
}()),
super.value(
key: key,
builder: builder,
value: value,
updateShouldNotify: updateShouldNotify,
child: child,
);


////listen为true则添加依赖,否则则读取数据
static T of<T>(BuildContext context, {bool listen = true}) {
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {///添加依赖,一旦继承式组件rebuild,contex会自动更新
context.dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
}
final value = inheritedElement?.value;
return value as T;
}
}

ProxyProvider

该组件功能包括 :

1、 Provider的功能

2、 可依赖上面其他模型(依赖于上面一个或多个Parrent Provider的数据。当其上面依赖的数据发送变化,自己携带的数据也会自动更新)。

3、依赖的数据应该继承Listenable、ChangeNotifier类型,否则无法监听数据变化

// {@template provider.proxyprovider}
/// A provider that builds a value based on other providers.
///
/// The exposed value is built through either `create` or `update`, then passed
/// to [InheritedProvider].
///
/// As opposed to `create`, `update` may be called more than once.
/// It will be called once the first time the value is obtained, then once
/// whenever [ProxyProvider] rebuilds or when one of the providers it depends on
/// updates.
///
/// [ProxyProvider] comes in different variants such as [ProxyProvider2]. This
/// is syntax sugar on the top of [ProxyProvider0].
///
/// As such, `ProxyProvider<A, Result>` is equal to:
/// ```dart
/// ProxyProvider0<Result>(
/// update: (context, result) {
/// final a = Provider.of<A>(context);
/// return update(context, a, result);
/// }
/// );
/// ```
///
/// Whereas `ProxyProvider2<A, B, Result>` is equal to:
/// ```dart
/// ProxyProvider0<Result>(
/// update: (context, result) {
/// final a = Provider.of<A>(context);
/// final b = Provider.of<B>(context);
/// return update(context, a, b, result);
/// }
/// );
/// ```
///
/// This last parameter of `update` is the last value returned by either
/// `create` or `update`.
/// It is `null` by default.
///
/// `update` must not be `null`.
///
/// See also:
///
/// * [Provider], which matches the behavior of [ProxyProvider] but has only
/// a `create` callback.
/// {@endtemplate}
class ProxyProvider<T, R> extends ProxyProvider0<R> {
/// Initializes [key] for subclasses.
ProxyProvider({
Key? key,
Create<R>? create,
required ProxyProviderBuilder<T, R> update,
UpdateShouldNotify<R>? updateShouldNotify,
Dispose<R>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
lazy: lazy,
builder: builder,
create: create,
update: (context, value) => update(
context,
Provider.of(context),///监听T的变化
value,
),
updateShouldNotify: updateShouldNotify,
dispose: dispose,
child: child,
);
}

ChangeNotifierProxyProvider

该组件功能包括 :

1、 ChangeNotifierProvider的功能

2、 可依赖上面其他模型(依赖于上面一个或多个Parrent Provider的数据。当其上面依赖的数据发送变化,自己携带的数据也会自动更新)。

class ChangeNotifierProxyProvider<T, R extends ChangeNotifier?>
extends ListenableProxyProvider<T, R>
{

ChangeNotifierProxyProvider({
Key? key,
required Create<R> create,
required ProxyProviderBuilder<T, R> update,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
create: create,
update: update,
dispose: ChangeNotifierProvider._dispose,
lazy: lazy,
builder: builder,
child: child,
);
}

MultiProvider避免多层嵌套

注意,由于继承自Nested类,会自动把providers数组中的Provider按序设置成父子关系,所以若数组元素中某个Provider含有child参数则会无效,因为Nested会自动把它下一个元素设置为它的child

/// A provider that merges multiple providers into a single linear widget tree.
/// It is used to improve readability and reduce boilerplate code of having to
/// nest multiple layers of providers.
///
/// As such, we're going from:
///
/// ```dart
/// Provider<Something>(
/// create: (_) => Something(),
/// child: Provider<SomethingElse>(
/// create: (_) => SomethingElse(),
/// child: Provider<AnotherThing>(
/// create: (_) => AnotherThing(),
/// child: someWidget,
/// ),
/// ),
/// ),
/// ```
///
/// To:
///
/// ```dart
/// MultiProvider(
/// providers: [
/// Provider<Something>(create: (_) => Something()),
/// Provider<SomethingElse>(create: (_) => SomethingElse()),
/// Provider<AnotherThing>(create: (_) => AnotherThing()),
/// ],
/// child: someWidget,
/// )
/// ```
class MultiProvider extends Nested {
MultiProvider({
Key? key,
required List<SingleChildWidget> providers,
Widget? child,///为了优化使用,当builder被多次调用时,child不会重新创建,会原封不动的传递给builder,然而此处builder并不会被多次调用,所以此处的优化其实并没有意义
TransitionBuilder? builder,///动态构建child
}) : super(
key: key,
children: providers,
child: builder != null
? Builder(///这里说明,builder和child参数都是可选的,优先使用builder,没有就使用child
builder: (context) => builder(context, child),
)
: child,
);
}

Consumer 监听所有数据变化

一个Consumer就是一个监听者,可以监听上面一个或多个数据模型的变化。

class Consumer<T> extends SingleChildStatelessWidget {
Consumer({
Key? key,
required this.builder,
Widget? child,
}) : super(key: key, child: child);

final Widget Function(
BuildContext context,
T value,
Widget? child,
) builder;

@override
Widget buildWithChild(BuildContext context, Widget? child) {
return builder(
context,
Provider.of<T>(context),///非常简单的监听者,代码简单,此处通过context监听了模型T的变化
child,
);
}
}

Selector 监听部分数据变化

1、一个Selector也是一个监听者,与Consumer不同的是,可选择性监听部分变化。它本身也是全量监听了模型的变化,只不过其内部做了判断,是否需要部分刷新,底层逻辑没有用到InheritedWidget中的Aspect字段。源码如下:

/// 在dart 3.0后要做到同时监听多个属性最简单的方式就是使用Dart语言中的`Records`类型(类似于swift中元组,C++结构体),而不用去重新定义一个新的类再重写==方法
/// ```dart
/// Selector<Foo, ({String item1, String item2})>(
/// selector: (_, foo) => (item1: foo.item1, item2: foo.item2),///监听item1和item2
/// builder: (_, data, __) {///只有item1或者item2发生变化,才会触发rebuild
/// return Text('${data.item1} ${data.item2}');
/// },
/// );
/// ```
class Selector<A, S> extends Selector0<S> {
Selector({
Key? key,
required ValueWidgetBuilder<S> builder,
required S Function(BuildContext, A) selector,
ShouldRebuild<S>? shouldRebuild,
Widget? child,
}) : super(
key: key,
shouldRebuild: shouldRebuild,
builder: builder,
selector: (context) => selector(context, Provider.of(context)),///此处最为关键,这里的context相当于中间商,它会监听模型A的任何变化,A一旦有任何改变contenx代表widget会被rebuild,但是内部会根据shouldRebuild来判断是否需要重新rebuild其child
child: child,
);
}

class Selector0<T> extends SingleChildStatefulWidget {
Selector0({
Key? key,
required this.builder,
required this.selector,
ShouldRebuild<T>? shouldRebuild,
Widget? child,///作为缓存优化使用,自动传给builder方法
}) : _shouldRebuild = shouldRebuild,
super(key: key, child: child);

final ValueWidgetBuilder<T> builder;///数据变化后,需要刷新则会调用重新构建子widget
final T Function(BuildContext) selector;///对哪些字段感兴趣?就返回他们
final ShouldRebuild<T>? _shouldRebuild;///如果感兴趣的内容没有发生变化则不需要更新

@override
_Selector0State<T> createState() => _Selector0State<T>();
}

class _Selector0State<T> extends SingleChildState<Selector0<T>> {
T? value;
Widget? cache;
Widget? oldWidget;

@override
Widget buildWithChild(BuildContext context, Widget? child) {
final selected = widget.selector(context);///用户感兴趣的数据

final shouldInvalidateCache = oldWidget != widget ||
(widget._shouldRebuild != null &&
widget._shouldRebuild!(value as T, selected)) ||
(widget._shouldRebuild == null &&
!const DeepCollectionEquality().equals(value, selected));///数据有没有发生变化
if (shouldInvalidateCache) {
value = selected;
oldWidget = widget;
cache = Builder(///重新构建新的Widget
builder: (context) => widget.builder(
context,
selected,
child,
),
);
}
return cache!;
}

}

2、部分监听,也可以使用context的select方法,源码如下,可以看到这个方法不如上面的方式灵活。但是底层使用了InheritedWidget Aspect参数来保存获取感兴趣的部分数据,例如,如果我想监听模型的属性A变化 且 B 没有变化时刷新UI,或者我想监听多个模型的某些属性变化 等复杂逻辑,下面的方法则无法支持。

extension SelectContext on BuildContext {
///如果一个widget只想监听 peson 模型的 name字段,但是不想监听age字段
/// 你就不能使用`context.watch`/[Provider.of]. 等方法
/// 我们可以使用 [select], 代码示例:
///
/// ```dart
/// Widget build(BuildContext context) {
/// final name = context.select((Person p) => p.name);
/// 当且仅当name字段变化时会自动刷新
/// return Text(name);
/// }
/// ```
/// It is fine to call `select` multiple times.
R select<T, R>(R Function(T value) selector) {
///从当前context找到最近的Provider<T>
final inheritedElement = Provider._inheritedElementOf<T>(this);
///取出里面的数据
final value = inheritedElement?.value;
///调用selector得到用户感兴趣的字段内容
final selected = selector(value);

if (inheritedElement != null) {
dependOnInheritedElement(////设置依赖,Aspect是一个函数
inheritedElement,
aspect: (T? newValue) {
return !const DeepCollectionEquality()
.equals(selector(newValue), selected);
},
);
} else {
// tell Flutter to rebuild the widget when relocated using GlobalKey
// if no provider were found before.
dependOnInheritedWidgetOfExactType<_InheritedProviderScope<T?>>();
}
return selected;
}
}

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Chris Scoot
Chris Scoot

Written by Chris Scoot

擅长C语言、C++、iOS开发(Objective-C\Swift)、Flutter开发(Dart语言)、GO语言、Python等,拥抱新技术,热爱AI领域,对OpenCV、Pytorch\Tensorflow充满热情。

No responses yet

What are your thoughts?