1. Scoped
Scoped 是使用了 AnimatedBuilder, 其原理是Listenable對象發(fā)出通知后, AnimatedBuilder調(diào)用state.setState()
.
// _AnimatedState
@override
void didUpdateWidget(AnimatedWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.listenable != oldWidget.listenable) {
oldWidget.listenable.removeListener(_handleChange);
widget.listenable.addListener(_handleChange);
}
}
void _handleChange() {
setState(() {
// The listenable's state is our build state, and it changed already.
});
}
2. Provider
Provider 則是Listenable對象發(fā)出通知后, 監(jiān)聽者調(diào)用element.markNeedsNotifyDependents()
函數(shù)將Builder
對應(yīng)的Element
標(biāo)記為dirty
, 在下一幀觸發(fā)對應(yīng)的Element
的performRebuild()
, 在performRebuild()
中調(diào)用built = build()
, 而build()
函數(shù)如下:
Widget build(BuildContext context) => builder(context);
調(diào)用了我們使用 Provider 時傳入的builder
.
3. Get
不管是 Scoped 還是 Provider, 都是使用InheritedWidget
持有數(shù)據(jù), InheritedWidget
是通過每個Element
都持有一個_ineritedWidgets
map 在整個 APP 內(nèi)同步數(shù)據(jù), 或許我們可以自己實現(xiàn)一套機制, 需要滿足以下條件.
- 通過少量條件即可在期望范圍內(nèi)的任何地方獲得數(shù)據(jù)的持有者或者數(shù)據(jù)本身
- 數(shù)據(jù)改變后可以通知到 UI 進行改變
如果想要實現(xiàn)任何地方
都能獲得數(shù)據(jù), 無疑使用單利或者全局的 map 比較好, 而需要數(shù)據(jù)修改后通知UI進行改變, 則Publisher-Subscriber模式也是一個優(yōu)秀的選擇.
很巧, Get的GetBuilder
正好符合以上期望.
Get 使用 GetInstance
的_singl
持有數(shù)據(jù)模型,
// GetInstance
static final Map<String, _InstanceBuilderFactory> _singl = {};
與 InheritedWidget 和 Scoped 通過Inherited
持有數(shù)據(jù)模型不同的是, GetBuilder
是通過 GetInstance
單例持有需要共享數(shù)據(jù). 這就造成GetBuilder
獲取模型需要考慮同級節(jié)點相同類型的問題, 除了使用模型的類型T
, 還需要一個tag
.
GetBuilder
中的tag
是可選的, 但在同級節(jié)點都使用相同類型的 Model 的時候必須使用tag
, 比如ListView
雖然每個
Element
都持有_ineritedWidgets
, 但父節(jié)點的_ineritedWidgets
都是被包含在子節(jié)點的_ineritedWidgets
中的, 邏輯上其實是一個樹結(jié)構(gòu), 通過類型T
是不會拿到同級節(jié)點相同類型T
的數(shù)據(jù)的.
Get 中的GetBuilder
通知 UI 刷新則使用的是與AnimatedBuilder
相同的原理, 收到 Model的通知后調(diào)用state.setState()
.
Get 除了GetBuilder
之外還有GetX
的響應(yīng)式狀態(tài)管理器, 在使用層面, 兩者的區(qū)別是前者需要明確調(diào)用GetxController.update()
觸發(fā)UI刷新, 而GetX
則只需要給需要監(jiān)聽的數(shù)據(jù)加上.obs
.
4. GetX 原理
a. Obx對_boserver
的監(jiān)聽
使用 GetX 時, 我們需要使用Obx
組件, 其集成自ObxWidget
, 代碼如下:
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key? key}) : super(key: key);
@override
_ObxState createState() => _ObxState();
@protected
Widget build();
}
class _ObxState extends State<ObxWidget> {
final _observer = RxNotifier();
late StreamSubscription subs;
@override
void initState() {
super.initState();
subs = _observer.listen(_updateTree, cancelOnError: false);
}
void _updateTree(_) {
if (mounted) {
setState(() {});
}
}
@override
void dispose() {
subs.cancel();
_observer.close();
super.dispose();
}
@override
Widget build(BuildContext context) =>
RxInterface.notifyChildren(_observer, widget.build);
}
可以看到ObxWidget
持有一個_ObxState
, _ObxState
持有final _observer = RxNotifier()
, 這就是一個通知者(或者說發(fā)布者), 在initState()
中對_observer
做了監(jiān)聽.
RxNotifier
內(nèi)部持有GetStream<T> subject = GetStream<T>()
, 我們其實是對subject
做了監(jiān)聽, 當(dāng)subject.add(event)
被調(diào)用時就會觸發(fā)監(jiān)聽.
b. _observer
對RxObject
的監(jiān)聽.
作為響應(yīng)式狀態(tài)管理方案, 響應(yīng)式對象, 即我們想要監(jiān)聽的數(shù)據(jù)可稱為RxObject
. 下面代碼中count
就是一個RxObject
.
var count = 0.obs;
作為一個響應(yīng)式方案,
GetX
中數(shù)據(jù)源也是RxNotifier
, 但RxNotifier
不一定是數(shù)據(jù)源
, 這樣應(yīng)該是為了組成響應(yīng)鏈.
但如果一個RxNotifier
作為數(shù)據(jù)源, 那就需要具備一些獨有的能力, 在GetX
中以_RxImpl
和RxObjectMixin
實現(xiàn)的, 其中_RxImpl
及一些列子類用來實現(xiàn)數(shù)據(jù)存儲和計算功能.
RxObjectMixin
則主要實現(xiàn)數(shù)據(jù)源的事件觸發(fā)的相關(guān)功能能, 所以GetX
的事件都來自RxObjectMixin
.
這里將所有with RxObjectMixin
的對象稱為RxObject
.
_observer
不是RxObject
, 它即不持有數(shù)據(jù), 也不會發(fā)出事件, GetX
中_observer
對RxObject
做了監(jiān)聽, 實現(xiàn)在_observer.addListener()
方法中, 由RxObject
主動調(diào)起.
// RxNotifier
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;
mixin NotifyManager<T> {
GetStream<T> subject = GetStream<T>();
final _subscriptions = <GetStream, List<StreamSubscription>>{};
bool get canUpdate => _subscriptions.isNotEmpty;
// RxObject調(diào)用這個方法, 這里實現(xiàn)了_oberver對RxObject的監(jiān)聽
void addListener(GetStream<T> rxGetx) {
if (!_subscriptions.containsKey(rxGetx)) {
final subs = rxGetx.listen((data) {
if (!subject.isClosed) subject.add(data);
});
final listSubscriptions =
_subscriptions[rxGetx] ??= <StreamSubscription>[];
listSubscriptions.add(subs);
}
}
// Obx調(diào)用這個方法, 監(jiān)聽_observer.
StreamSubscription<T> listen(
void Function(T) onData, {
Function? onError,
void Function()? onDone,
bool? cancelOnError,
}) =>
subject.listen(
onData,
onError: onError,
onDone: onDone,
cancelOnError: cancelOnError ?? false,
);
/// Closes the subscriptions for this Rx, releasing the resources.
void close() {
_subscriptions.forEach((getStream, _subscriptions) {
for (final subscription in _subscriptions) {
subscription.cancel();
}
});
_subscriptions.clear();
subject.close();
}
}
_observer.addListener()
是由RxObject
主動調(diào)起的. 具體過程是在Obx
首次創(chuàng)建時, 會調(diào)用build()
函數(shù), 實際調(diào)用了RxInterface.notifyChildren()
, 并傳入_observer
.
@override
Widget build(BuildContext context) =>
RxInterface.notifyChildren(_observer, widget.build);
在RxInterface.notifyChildren()
中, 將_observer
賦值給RxInterface.proxy
, 這么做主要是為了在RxObject
通過訪問靜態(tài)變量RxInterface.proxy
就可以獲得對應(yīng)的_observer
.
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final _observer = RxInterface.proxy;
RxInterface.proxy = observer;
// 這里觸發(fā) RxObject.value 的 get 方法
final result = builder();
if (!observer.canUpdate) {
RxInterface.proxy = _observer;
throw """
[Get] the improper use of a GetX has been detected.
You should only use GetX or Obx for the specific widget that will be updated.
If you are seeing this error, you probably did not insert any observable variables into GetX/Obx
or insert them outside the scope that GetX considers suitable for an update
(example: GetX => HeavyWidget => variableObservable).
If you need to update a parent widget and a child widget, wrap each one in an Obx/GetX.
""";
}
RxInterface.proxy = _observer;
return result;
}
在final result = builder()
這行代碼中, 調(diào)用了我們傳入Obx
的builder
, 一般builder
可以這樣寫:
Obx(() {
return Text('count: $count');
})
這最終調(diào)用了RxObject.value
的get
方法.
定義一個
RxObject
:RxObject obj
, 當(dāng)我們在表達式中直接使用obj
其實是調(diào)用了這個類型的call()
函數(shù), 而$obj
則相當(dāng)于obj.call().toString()
.
RxObject
的關(guān)鍵代碼:
T call([T? v]) {
if (v != null) {
value = v;
}
return value;
}
String get string => value.toString();
@override
String toString() => value.toString();
dynamic toJson() => value;
set value(T val) {
if (subject.isClosed) return;
sentToStream = false;
if (_value == val && !firstRebuild) return;
firstRebuild = false;
_value = val;
sentToStream = true;
subject.add(_value);
}
/// Returns the current [value]
T get value {
RxInterface.proxy?.addListener(subject);
return _value;
}
通過call()
函數(shù), 我們觸發(fā)了T get value
方法. 然后調(diào)動了RxInterface.proxy?.addListener(subject)
, 最終完成了_observer
對RxObject
的監(jiān)聽.
c. 發(fā)送事件
通過 a 和 b 兩步, 已經(jīng)完成了整個監(jiān)聽鏈條, 最終則是觸發(fā)事件, 或者說發(fā)送事件.
以 RxInt
為例, 我們?nèi)绻O(jiān)聽一個int
類型的數(shù)據(jù), 需要寫為var num = 0.obs
, obs
函數(shù)返回的就是一個RxInt
對象(集成自RxObject), 其內(nèi)部實現(xiàn)了int
類型的運算, 下面是加法運算:
RxInt operator +(int other) {
value = value + other;
return this;
}
其中value = value + other
對value
做了賦值, 毫無疑問會觸發(fā)RxObject.value
的set
方法, 當(dāng)我們調(diào)用num++
, 最終會在set
方法中調(diào)用subject.add(_value)
, 完成了事件發(fā)送.
監(jiān)聽鏈: