Riverpod
数据共享也是使用了InheritedWidget
,在项目中,runapp外层要嵌套一个ProviderScope
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
而ProviderScope
是个StatefulWidget
,在State build方法中,使用了UncontrolledProviderScope
UncontrolledProviderScope
就是一个InheritedWidget
在我们使用
riverpod
时候,获取的状态都保存在这个UncontrolledProviderScope
中,具体是ProviderContainer
对象当中,这里能够感受到的一个最大的优势就是不再依赖上下文,因为在任何地方获取的都是这个顶层的状态,像之前如果路由进行了跳转,路由1中的状态是没办法在路由2中获取到的,当然有方法去解决,但是会带来更多的代码和结构上的不合理.
riverpod
提供的是顶层的InheritedWidget
来管理所有的状态,很容易想到ProviderContainer
中应该是有个Map来存储所有的状态,所有状态保存在顶层,下层组件想要使用状态,通过顶层的InheritedWidget获取ProviderContainer
来使用
riverpod
里边一个很特别的对象就是ref
,在provider
(指的riverpod中的状态)中有ref
,在Consumer
中也有一个ref
,但是这两个ref
是不一样的类型,这里也要说一下riverpod
中的provider
会有对应的element
,所以可以简单的理解
provider
中的ref
是providerElement
Consumer
中的ref
是widgetElement
,也就是说context
可以被强制转化成ref
关于providerElement
可以类比widget与element的关系,provider只是一个配置类,providerElement才是真实的状态管理类,只有providerElement创建才会产生真正的状态,所以在Riverpod
中经常会看见类似这样的代码
final counterStateProvider = StateProvider<int>((ref)=>0)
StateProvider
可能会被提前创建出来,但是真正的状态被创建,会在真正使用的时候
所以不必担心全局创建的provider
这里还是要做一个类比
InheritedElement
与WidgetElement
的关系与ProviderElement
与ConsumerStatefulElement
InheritedElement
中有_dependents
,WidgetElement
中有_dependencies
ProviderElement
中有_dependents
,ConsumerStatefulElement
中有_dependencies
他们的含义是一样的_dependents
代表哪些组件注册了刷新回调,_dependencies
代表组件在哪些状态中注册了回调,有点绕,但是要理解
上边说了在顶层有一个保存所有状态的InheritedWidget
,但是具体如何操作组件刷新的呢?
类比Provider
状态管理,Riverpod
也提供了两个方法read
和watch
,含义是一样的read
是用来读数据,watch
不仅用来读也用来注册刷新回调
在ConsumerWidget
中使用的ref
我们上面说到是widget
的element
,我们执行ref.watch(countProvider)
的时候
@override
Res watch<Res>(ProviderListenable<Res> target) {
_assertNotDisposed();
return _dependencies.putIfAbsent(target, () {
final oldDependency = _oldDependencies?.remove(target);
if (oldDependency != null) {
return oldDependency;
}
return _container.listen<Res>(
target,
(_, __) => markNeedsBuild(),
);
}).read() as Res;
}
_container
就是顶层的ProviderContainer
_dependencies
用来存放_container
监听之后的ProviderSubscription
,类比StreamSubscription
,如果当前的element
从element tree移除后,可以移除掉在_container
中注册的刷新回调
@override
ProviderSubscription<State> listen<State>(
ProviderListenable<State> provider,
void Function(State? previous, State next) listener, {
bool fireImmediately = false,
void Function(Object error, StackTrace stackTrace)? onError,
}) {
return provider.addListener(
this,
listener,
fireImmediately: fireImmediately,
onError: onError,
onDependencyMayHaveChanged: null,
);
}
最终走到Provider
的addListener
方法 node.readProviderElement(this)
本质就是找到ProviderElement
,这里其实也发现了ProviderElement
与WidgetElement
的一一对应的关系是_ProviderStateSubscription
来管理的
_ProviderStateSubscription(
super.source, {
required this.listenedElement,
required this.listener,
required this.onError,
}) {
final dependents = listenedElement._dependents ??= [];
dependents.add(this);
}
构造函数可以看到_dependents
添加了Subscription
,也就是添加了组件的刷新回调,provider
中的状态变化的时候可以遍历_dependents
来刷新UI组件,实际也是这样做的
void _notifyListeners(
Result<StateT> newState,
Result<StateT>? previousStateResult, {
bool checkUpdateShouldNotify = true,
}) {
...
final listeners = _dependents?.toList(growable: false);
newState.map(
data: (newState) {
if (listeners != null) {
for (var i = 0; i < listeners.length; i++) {
final listener = listeners[i];
if (listener is _ProviderStateSubscription) {
Zone.current.runBinaryGuarded(
listener.listener,
previousState,
newState.state,
);
}
}
}
},
error: (newState) {
if (listeners != null) {
for (var i = 0; i < listeners.length; i++) {
final listener = listeners[i];
if (listener is _ProviderStateSubscription<StateT>) {
Zone.current.runBinaryGuarded(
listener.onError,
newState.error,
newState.stackTrace,
);
}
}
}
},
);
...
}
用StateProvider
举例,在我们调用ref.read(countProvider.notifier).state++
的时候会执行listenerEntry.listener(value)
set state(T value) {
assert(_debugIsMounted(), '');
final previousState = _state;
_state = value;
final errors = <Object>[];
final stackTraces = <StackTrace?>[];
for (final listenerEntry in _listeners) {
try {
listenerEntry.listener(value);
} catch (error, stackTrace) {
errors.add(error);
stackTraces.add(stackTrace);
if (onError != null) {
onError!(error, stackTrace);
} else {
Zone.current.handleUncaughtError(error, stackTrace);
}
}
}
if (errors.isNotEmpty) {
throw StateNotifierListenerError._(errors, stackTraces, this);
}
}
最终会调用到ProviderElement
的setState
,然后调用_notifyListeners
void setState(StateT newState) {
assert(
() {
_debugDidSetState = true;
return true;
}(),
'',
);
final previousResult = getState();
final result = _state = ResultData(newState);
if (_didBuild) {
_notifyListeners(result, previousResult);
}
}
而setState
是在ProviderElement
创建的时候就进行了注册
void create({required bool didChangeDependency}) {
final provider = this.provider as _StateProviderBase<T>;
final initialState = provider._create(this);
final controller = StateController(initialState);
_controllerNotifier.result = Result.data(controller);
_removeListener = controller.addListener(
fireImmediately: true,
(state) {
_stateNotifier.result = _controllerNotifier.result;
setState(state);
},
);
}
Riverpod
实际做的对系统InheritedWidget
的优化,让状态从组件数中抽离出来,
InheritedWidget
时代,状态和组件本身就是一个东西(InheritedElement
就是个特殊的WidgetElement
),自己管理自己的状态,也就导致他严重依赖组件树的结构,如果两个处于不同分支下的状态想要相互调用是不可以的,但是Riverpod
对他们进行了拆分,状态单独出来,并且通过索引保存在最顶层,这样既保持了状态和组件的1对1的关系,又实现了不同分支下的状态之间的相互调用