在flutter开发中状态管理框架Provider应该大家都接触过,这里我就结合我在使用过程中,出现的疑惑,以及一步一步找到答案的过程,来详细的介绍一下Provider框架实现的原理
大家应该都知道Provider是基于InheritedWidget实现的,所以我最开始的思路完全是按照InheritedWidget来理解这个框架的,如果对InheritedWidget不是很了解,可以看看之前的文章InheritedWidget
Provider中value是一个ChangeNotifier,然后在需要刷新的时候调用notifyListeners来刷新,结合InheritedWidget,我的思路是ChangeNotifier监听InheritedWidget的父Element,notifyListeners来调用父Element的markNeedsBuild,实现数据刷新
但是通过源码发现,Provider内部并没有给InheritedWidget包裹Widget,而是直接监听的InheritedWidget(InheritedElement),调用的也是InheritedElement的markNeedsBuild
class ChangeNotifierProvider<T extends ChangeNotifier?>
extends ListenableProvider<T>
在ListenableProvider的构造函数中,有一个_startListening,这个方法中实现了回调监听,一旦ChangeNotifier执行了notifyListeners,_startListening就会被调用
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
/// Creates a [Listenable] using [create] and subscribes to it.
///
/// [dispose] can optionally passed to free resources
/// when [ListenableProvider] is removed from the tree.
///
/// [create] must not be `null`.
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,
);
static VoidCallback _startListening(
InheritedContext e,
Listenable? value,
) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
而markNeedsNotifyDependents本质就是调用了markNeedsBuild
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
因为最"正宗"的使用InheritedWidget,是通过setState这个方法去刷新,会调用InheritedWidget(InheritedElement)的update方法,这个方法会将使用InheritedWidget的element设置成_dirty,从而实现引用的组件刷新,这里没有父组件的刷新,因此也不会执行update方法,因此Provider一定是在强制刷新的方法中做了手脚,果不其然,就在performRebuild执行build中
void performRebuild() {
Widget? built;
try {
assert(() {
_debugDoingBuild = true;
return true;
}());
built = build();
assert(() {
_debugDoingBuild = false;
return true;
}());
debugWidgetBuilderValue(widget, built);
} catch (e, stack) {
_debugDoingBuild = false;
built = ErrorWidget.builder(
_reportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
super.performRebuild(); // clears the "dirty" flag
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
built = ErrorWidget.builder(
_reportException(
ErrorDescription('building $this'),
e,
stack,
informationCollector: () => <DiagnosticsNode>[
if (kDebugMode)
DiagnosticsDebugCreator(DebugCreator(this)),
],
),
);
_child = updateChild(null, built, slot);
}
}
Provider 重写了build方法,这里边执行了notifyClients,去将监听的element强制刷新的
@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();
}
到这里其实大概的原理就解释通了,
- ChangeNotifier负责数据以及刷新InheritedElement
- InheritedElement负责向下传递数据以及更新使用数据的Element,但是是对InheritedElement做了小小的改动的
补充:
其实这样做还有一个好处,就是本身InheritedElement重构逻辑会导致下面所有的element重构,但是Provider直接通过_dirty来刷新,避免了这一重构灾难(虽然创建widget tree 不是很消耗资源和性能),可以尽量做到那里需要刷新就刷新那里,也算是一个亮点吧
网友评论