1. Scoped
Scoped 是使用了 AnimatedBuilder, 其原理是Listenable对象发出通知后, AnimatedBuilder调用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对象发出通知后, 监听者调用element.markNeedsNotifyDependents()
函数将Builder
对应的Element
标记为dirty
, 在下一帧触发对应的Element
的performRebuild()
, 在performRebuild()
中调用built = build()
, 而build()
函数如下:
Widget build(BuildContext context) => builder(context);
调用了我们使用 Provider 时传入的builder
.
3. Get
不管是 Scoped 还是 Provider, 都是使用InheritedWidget
持有数据, InheritedWidget
是通过每个Element
都持有一个_ineritedWidgets
map 在整个 APP 内同步数据, 或许我们可以自己实现一套机制, 需要满足以下条件.
- 通过少量条件即可在期望范围内的任何地方获得数据的持有者或者数据本身
- 数据改变后可以通知到 UI 进行改变
如果想要实现任何地方
都能获得数据, 无疑使用单利或者全局的 map 比较好, 而需要数据修改后通知UI进行改变, 则Publisher-Subscriber模式也是一个优秀的选择.
很巧, Get的GetBuilder
正好符合以上期望.
Get 使用 GetInstance
的_singl
持有数据模型,
// GetInstance
static final Map<String, _InstanceBuilderFactory> _singl = {};
与 InheritedWidget 和 Scoped 通过Inherited
持有数据模型不同的是, GetBuilder
是通过 GetInstance
单例持有需要共享数据. 这就造成GetBuilder
获取模型需要考虑同级节点相同类型的问题, 除了使用模型的类型T
, 还需要一个tag
.
GetBuilder
中的tag
是可选的, 但在同级节点都使用相同类型的 Model 的时候必须使用tag
, 比如ListView
虽然每个
Element
都持有_ineritedWidgets
, 但父节点的_ineritedWidgets
都是被包含在子节点的_ineritedWidgets
中的, 逻辑上其实是一个树结构, 通过类型T
是不会拿到同级节点相同类型T
的数据的.
Get 中的GetBuilder
通知 UI 刷新则使用的是与AnimatedBuilder
相同的原理, 收到 Model的通知后调用state.setState()
.
Get 除了GetBuilder
之外还有GetX
的响应式状态管理器, 在使用层面, 两者的区别是前者需要明确调用GetxController.update()
触发UI刷新, 而GetX
则只需要给需要监听的数据加上.obs
.
4. GetX 原理
a. Obx对_boserver
的监听
使用 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()
, 这就是一个通知者(或者说发布者), 在initState()
中对_observer
做了监听.
RxNotifier
内部持有GetStream<T> subject = GetStream<T>()
, 我们其实是对subject
做了监听, 当subject.add(event)
被调用时就会触发监听.
b. _observer
对RxObject
的监听.
作为响应式状态管理方案, 响应式对象, 即我们想要监听的数据可称为RxObject
. 下面代码中count
就是一个RxObject
.
var count = 0.obs;
作为一个响应式方案,
GetX
中数据源也是RxNotifier
, 但RxNotifier
不一定是数据源
, 这样应该是为了组成响应链.
但如果一个RxNotifier
作为数据源, 那就需要具备一些独有的能力, 在GetX
中以_RxImpl
和RxObjectMixin
实现的, 其中_RxImpl
及一些列子类用来实现数据存储和计算功能.
RxObjectMixin
则主要实现数据源的事件触发的相关功能能, 所以GetX
的事件都来自RxObjectMixin
.
这里将所有with RxObjectMixin
的对象称为RxObject
.
_observer
不是RxObject
, 它即不持有数据, 也不会发出事件, GetX
中_observer
对RxObject
做了监听, 实现在_observer.addListener()
方法中, 由RxObject
主动调起.
// 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调用这个方法, 这里实现了_oberver对RxObject的监听
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调用这个方法, 监听_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
主动调起的. 具体过程是在Obx
首次创建时, 会调用build()
函数, 实际调用了RxInterface.notifyChildren()
, 并传入_observer
.
@override
Widget build(BuildContext context) =>
RxInterface.notifyChildren(_observer, widget.build);
在RxInterface.notifyChildren()
中, 将_observer
赋值给RxInterface.proxy
, 这么做主要是为了在RxObject
通过访问静态变量RxInterface.proxy
就可以获得对应的_observer
.
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final _observer = RxInterface.proxy;
RxInterface.proxy = observer;
// 这里触发 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()
这行代码中, 调用了我们传入Obx
的builder
, 一般builder
可以这样写:
Obx(() {
return Text('count: $count');
})
这最终调用了RxObject.value
的get
方法.
定义一个
RxObject
:RxObject obj
, 当我们在表达式中直接使用obj
其实是调用了这个类型的call()
函数, 而$obj
则相当于obj.call().toString()
.
RxObject
的关键代码:
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()
函数, 我们触发了T get value
方法. 然后调动了RxInterface.proxy?.addListener(subject)
, 最终完成了_observer
对RxObject
的监听.
c. 发送事件
通过 a 和 b 两步, 已经完成了整个监听链条, 最终则是触发事件, 或者说发送事件.
以 RxInt
为例, 我们如果监听一个int
类型的数据, 需要写为var num = 0.obs
, obs
函数返回的就是一个RxInt
对象(集成自RxObject), 其内部实现了int
类型的运算, 下面是加法运算:
RxInt operator +(int other) {
value = value + other;
return this;
}
其中value = value + other
对value
做了赋值, 毫无疑问会触发RxObject.value
的set
方法, 当我们调用num++
, 最终会在set
方法中调用subject.add(_value)
, 完成了事件发送.
监听链: