中秋節大家有好好賞月嗎,趁著連假有空的時候,繼續把 Riverpod 內容繼續讀完,上班時間可沒那麼多體裡可以把它嗑完。那廢話不多說就開始吧!
ProviderContainer 是如何交互的
由昨天所提到的部分,我們可以知道 ProviderContainer
才是那個真正記錄所有狀態的核心,不過在平常開發中,大部分的時間我們並沒有直接操作他,而是透過 ref.raed
或 ref.watch
,去查看當前 provider 的狀態,今天要講的部分就是這中間的過程是如何完成的。
如何建立 Provider
首先先從如何建立 Provider 開始看起,要建立每一個 Provider 都需要提供一個 _createFn: (ref)⇒false
,那 _createFn
實際上到底是做了什麼呢?
final myProvider = Provider<bool>((ref) => false);
那我們就必須深入 Provider 的程式碼裡面,在這裡你可以看到每個 Provider 類型都被需要去繼承一個方法就是 _create
,這裡定義了在每個 Provider 被建立出實體時,也會一併去建立一個相對應的 ProviderElement
。
class Provider<State> extends InternalProvider<State>
with AlwaysAliveProviderBase<State> {
/// {@macro riverpod.provider}
Provider(
this._createFn, {
super.name,
super.dependencies,
@Deprecated('Will be removed in 3.0.0') super.from,
@Deprecated('Will be removed in 3.0.0') super.argument,
@Deprecated('Will be removed in 3.0.0') super.debugGetCreateSourceHash,
}) : super(
allTransitiveDependencies:
computeAllTransitiveDependencies(dependencies),
);
...
final Create<State, ProviderRef<State>> _createFn;
@override
State _create(ProviderElement<State> ref) => _createFn(ref);
@override
ProviderElement<State> createElement() => ProviderElement(this);
...
}
Provide 與 Element的關聯
這裡的 element 與三棵樹里的 element 並不相同,不過我認為取這個名稱也是有意義的,因為 Provider 本身就像是 Widget 一樣只是描述如何建立一個 State,Element 才是管理真正實際內容的地方。
State 更新機制
另一個有意思的地方,在每一個 ProvideElement 中,如果 state 被更改了了,有一個 setState
用 _notifyListeners
提醒訂閱者,也是和 Flutter 的 setState
相互呼應。
void setState(State newState) {
assert(
() {
_debugDidSetState = true;
return true;
}(),
'',
);
final previousResult = getState();
final result = _state = ResultData(newState);
if (_didBuild) {
_notifyListeners(result, previousResult);
}
}
小總結
現在我們先一起喘口氣,對目前為止的內容做一下小總結:
ProviderScope
對 WidgetTree 注入 ProviderContainer
ProviderContainer
掌管所有的 Provider state_createFn
建立 ProviderElement
管理當前的狀態ProviderElement
的人 State 已經被更新Widget 與 ProviderElement 的連接
深吸一口氣,我們一起來看最後一個關鍵,Widget 要怎麼跟 ProviderElement 串連起來,達到更新 Widget 的方法。
ConsumerStatefulWidget 的角色
那接下來的步驟,大家應該都猜到了吧!下一個要解剖的就是 ConsumerStatefulWidget
啦。你說為什麼不看 ConsumerWidget
? 因為 ConsumerWidget
也是繼承自 ConsumerStatefulWidget
🌝
abstract class ConsumerWidget extends ConsumerStatefulWidget {
...
那 ConsumerStatefulWidget
實際上做了什麼呢?他 override 了 createElement 的方法,替換成 ConsumerStatefulElement
,以達到我們先前說的 ProviderElement
的注入。
abstract class ConsumerStatefulWidget extends StatefulWidget {
/// A [StatefulWidget] that can read providers.
const ConsumerStatefulWidget({super.key});
@override
// ignore: no_logic_in_create_state
ConsumerState createState();
@override
ConsumerStatefulElement createElement() {
return ConsumerStatefulElement(this);
}
}
另一點則是在 _ConsumerState
裡面放入 ref,作為我們可以拿來操作的對象,下一段會說明為什麼 Buildcontext
可以直接變成 WidgetRef
class _ConsumerState extends ConsumerState<ConsumerWidget> {
@override
WidgetRef get ref => context as WidgetRef;
@override
Widget build(BuildContext context) {
return widget.build(context, ref);
}
}
WidgetRef 的定義與功能
這裡可以看到 ConsumerStatefulElement
繼承了 StatefulElement
並實作 WidgetRef
。我們知道 StatefulElement
是 BuildContext
的子類別,所以 WidgetRef
也是 BuildContext
的一個子類別的實現。
簡言之 WidgetRef
就是 BuildContext
的擴展,讓他可以勝任操作 watch, read 與 ProviderContainer
互動的功能。
/// The [Element] for a [ConsumerStatefulWidget]
class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
/// The [Element] for a [ConsumerStatefulWidget]
ConsumerStatefulElement(ConsumerStatefulWidget super.widget);
late ProviderContainer _container = ProviderScope.containerOf(this);
...
}
所以我們可以看到在 ConsumerStatefulElement
在 didChangeDependencies
就會去重新讀取 ProviderContainer
,以達到更新 Provider 內容的作用。
class ConsumerStatefulElement extends StatefulElement implements WidgetRef {
...
@override
void didChangeDependencies() {
super.didChangeDependencies();
final newContainer = ProviderScope.containerOf(this);
if (_container != newContainer) {
_container = newContainer;
for (final dependency in _dependencies.values) {
dependency.close();
}
_dependencies.clear();
}
}
至此大家應該對 Riverpod 如何透過 Element 達到串連 Widget 的效果有了初步的理解。這次的 Riverpod 解析也先告一段落,下次有機會再繼續展開!
在本文中,我們深入探討了 Riverpod 狀態管理工具的核心概念與運作機制。透過對ProviderContainer
、Provider
、ProviderElement
以及ConsumerStatefulWidget
的解析,可以看出 Riverpod 是如何與 Flutter 的 Widget 系統結合,提供靈活且強大的狀態管理。
ProviderContainer
確實成為了狀態管理的核心,儲存且跟踪所有providers的狀態。ref.read
和ref.watch
功能,使得開發者能夠輕鬆地查看和互動當前的provider狀態。ConsumerStatefulWidget
和ConsumerStatefulElement
,Riverpod 與 Flutter 建立了密切的聯繫,使得 Widgets 可以更加直觀地反映狀態變化。希望你們喜歡今天的分享,明天繼續 🌝