Riverpod
數(shù)據(jù)共享也是使用了InheritedWidget
,在項(xiàng)目中,runapp外層要嵌套一個(gè)ProviderScope
void main() {
runApp(
ProviderScope(
child: MyApp(),
),
);
}
而ProviderScope
是個(gè)StatefulWidget
,在State build方法中,使用了UncontrolledProviderScope
UncontrolledProviderScope
就是一個(gè)InheritedWidget
在我們使用
riverpod
時(shí)候,獲取的狀態(tài)都保存在這個(gè)UncontrolledProviderScope
中,具體是ProviderContainer
對象當(dāng)中,這里能夠感受到的一個(gè)最大的優(yōu)勢就是不再依賴上下文,因?yàn)樵谌魏蔚胤将@取的都是這個(gè)頂層的狀態(tài),像之前如果路由進(jìn)行了跳轉(zhuǎn),路由1中的狀態(tài)是沒辦法在路由2中獲取到的,當(dāng)然有方法去解決,但是會(huì)帶來更多的代碼和結(jié)構(gòu)上的不合理.
riverpod
提供的是頂層的InheritedWidget
來管理所有的狀態(tài),很容易想到ProviderContainer
中應(yīng)該是有個(gè)Map來存儲(chǔ)所有的狀態(tài),所有狀態(tài)保存在頂層,下層組件想要使用狀態(tài),通過頂層的InheritedWidget獲取ProviderContainer
來使用
riverpod
里邊一個(gè)很特別的對象就是ref
,在provider
(指的riverpod中的狀態(tài))中有ref
,在Consumer
中也有一個(gè)ref
,但是這兩個(gè)ref
是不一樣的類型,這里也要說一下riverpod
中的provider
會(huì)有對應(yīng)的element
,所以可以簡單的理解
provider
中的ref
是providerElement
Consumer
中的ref
是widgetElement
,也就是說context
可以被強(qiáng)制轉(zhuǎn)化成ref
關(guān)于providerElement
可以類比widget與element的關(guān)系,provider只是一個(gè)配置類,providerElement才是真實(shí)的狀態(tài)管理類,只有providerElement創(chuàng)建才會(huì)產(chǎn)生真正的狀態(tài),所以在Riverpod
中經(jīng)常會(huì)看見類似這樣的代碼
final counterStateProvider = StateProvider<int>((ref)=>0)
StateProvider
可能會(huì)被提前創(chuàng)建出來,但是真正的狀態(tài)被創(chuàng)建,會(huì)在真正使用的時(shí)候
所以不必?fù)?dān)心全局創(chuàng)建的provider
這里還是要做一個(gè)類比
InheritedElement
與WidgetElement
的關(guān)系與ProviderElement
與ConsumerStatefulElement
InheritedElement
中有_dependents
,WidgetElement
中有_dependencies
ProviderElement
中有_dependents
,ConsumerStatefulElement
中有_dependencies
他們的含義是一樣的_dependents
代表哪些組件注冊了刷新回調(diào),_dependencies
代表組件在哪些狀態(tài)中注冊了回調(diào),有點(diǎn)繞,但是要理解
上邊說了在頂層有一個(gè)保存所有狀態(tài)的InheritedWidget
,但是具體如何操作組件刷新的呢?
類比Provider
狀態(tài)管理,Riverpod
也提供了兩個(gè)方法read
和watch
,含義是一樣的read
是用來讀數(shù)據(jù),watch
不僅用來讀也用來注冊刷新回調(diào)
在ConsumerWidget
中使用的ref
我們上面說到是widget
的element
,我們執(zhí)行ref.watch(countProvider)
的時(shí)候
@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
監(jiān)聽之后的ProviderSubscription
,類比StreamSubscription
,如果當(dāng)前的element
從element tree移除后,可以移除掉在_container
中注冊的刷新回調(diào)
@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)
本質(zhì)就是找到ProviderElement
,這里其實(shí)也發(fā)現(xiàn)了ProviderElement
與WidgetElement
的一一對應(yīng)的關(guān)系是_ProviderStateSubscription
來管理的
_ProviderStateSubscription(
super.source, {
required this.listenedElement,
required this.listener,
required this.onError,
}) {
final dependents = listenedElement._dependents ??= [];
dependents.add(this);
}
構(gòu)造函數(shù)可以看到_dependents
添加了Subscription
,也就是添加了組件的刷新回調(diào),provider
中的狀態(tài)變化的時(shí)候可以遍歷_dependents
來刷新UI組件,實(shí)際也是這樣做的
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
舉例,在我們調(diào)用ref.read(countProvider.notifier).state++
的時(shí)候會(huì)執(zhí)行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);
}
}
最終會(huì)調(diào)用到ProviderElement
的setState
,然后調(diào)用_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
創(chuàng)建的時(shí)候就進(jìn)行了注冊
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
實(shí)際做的對系統(tǒng)InheritedWidget
的優(yōu)化,讓狀態(tài)從組件數(shù)中抽離出來,
InheritedWidget
時(shí)代,狀態(tài)和組件本身就是一個(gè)東西(InheritedElement
就是個(gè)特殊的WidgetElement
),自己管理自己的狀態(tài),也就導(dǎo)致他嚴(yán)重依賴組件樹的結(jié)構(gòu),如果兩個(gè)處于不同分支下的狀態(tài)想要相互調(diào)用是不可以的,但是Riverpod
對他們進(jìn)行了拆分,狀態(tài)單獨(dú)出來,并且通過索引保存在最頂層,這樣既保持了狀態(tài)和組件的1對1的關(guān)系,又實(shí)現(xiàn)了不同分支下的狀態(tài)之間的相互調(diào)用