GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。本文来解析下Getx是怎样实现的状态管理。老规矩上Counter Demo。
class Controller extends GetxController{
var count = 0.obs;
increment() => count++;
}
class Home extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final Controller c = Get.put(Controller());
return Scaffold(
// 使用Obx(()=>每当改变计数时,就更新Text()。
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count}"))),
// 用一个简单的Get.to()即可代替Navigator.push那8行,无需上下文!
body: Center(child: ElevatedButton(
child: Text("Go to Other"), onPressed: () => Get.to(Other()))),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.add), onPressed: c.increment));
}
从头捋捋,以Obx入口,我们将需要刷新的内容用Obx包裹实现状态刷新重建。Obx是一个继承自ObxWidget的widget,用builder实现build方法。
class Obx extends ObxWidget {
final WidgetCallback builder;
const Obx(this.builder);
@override
Widget build() => builder();
}
ObxWidget是是所有 GetX 反应式小部件的基础。ObxWidget的State中新建一个RxNotifier类型的_observer,initState添加_observer监听事件,将事件回调设置为_updateTree,可以看到_updateTree实际就是setState的实现,即_observer收到事件会触发当前ObxWidget组件rebuild。到这里我们大概能猜知道Getx的刷新实际是利用StatefulWidget setState的实现。接下来我们接着探索Getx是如何将count状态与ObxWidget关联起来的。
abstract class ObxWidget extends StatefulWidget {
const ObxWidget({Key? key}) : super(key: key);
...
}
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 State build方法中,调用RxInterface静态build方法创建布局,将_observer和build方法作为参数传递。notifyChildren是将_observer与状态建立联系的方法。RxInterface有proxy类型,这里创建了一个temp引用,然后再将_observer赋值给proxy。可能这里有些同学会有点疑问,observer跟proxy类型都不一样,怎么能赋值?实际上observer是RxInterface的子类,后面会分析到。接着调用builder() build我们传过来的widget。接着判断observer是否canUpdate。不能的话抛出错误,恢复proxy变量。如果有观察者则返回builder生成的result widget;
abstract class RxInterface<T> {
static RxInterface? proxy;
...
/// Avoids an unsafe usage of the `proxy`
static T notifyChildren<T>(RxNotifier observer, ValueGetter<T> builder) {
final _observer = RxInterface.proxy;
RxInterface.proxy = observer;
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;
}
}
_observer是RxNotifier类型,RxNotifier的定义看着是不是跟平时的类定义不太一样。NotifyManager是一个mixin,RxInterface是抽象类。应该有小伙伴猜到了吧。实际上就是RxNotifier用NotifyManager实现了RxInterface的抽象方法。可以看到NotifyManager新建了GetStream(Get自己实现的Stream类) subject和一个_subscriptions。
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;
/// This is an internal method.
/// Subscribe to changes on the inner stream.
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);
}
}
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();
}
}
目前为止一切都还没有关联,关键来了,我们在Controller中0.obs实际是Getx对int的扩展(extension),obs方法会返回一个Rx对象(Getx的拓展包括String,int,double,bool,任意类型T,List,Map,Set…具体可参照文档或get_rx包)。
Rx类,Rx是一个抽象类继承自_RxImpl,_RxImpl同样是抽象类,可以看到_RxImpl同样是继承RxNotifier,不同的是mixin进RxObjectMixin,RxObjectMixin是对value操作的mixin。value set方法中会将新的value通过subject发送出去,value get方法,这里调用了RxInterface.proxy?.addListener(subject);这里就是将当前Rx对象的subject传递给ObxWidget的_observer。当subject发生变化时会通知_observer,_observer的subject则发送事件刷新树。这里还有个问题我们什么时候调用了value的get方法呢?答案就是我们调用RxInterface的notifyChildren时候,final result = builder();当我们调用builder会value get方法,从而关联刷新。
mixin RxObjectMixin<T> on NotifyManager<T> {
late T _value;
void refresh() {
subject.add(value);
}
T call([T? v]) {
if (v != null) {
value = v;
}
return value;
}
bool firstRebuild = true;
bool sentToStream = false;
...
set value(T val) {
if (subject.isClosed) return;
sentToStream = false;
if (_value == val && !firstRebuild) return;
firstRebuild = false;
_value = val;
sentToStream = true;
subject.add(_value);
}
T get value {
RxInterface.proxy?.addListener(subject);
return _value;
}
...
}
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;
mixin NotifyManager<T> {
...
}
abstract class _RxImpl<T> extends RxNotifier<T> with RxObjectMixin<T> {
_RxImpl(T initial) {
_value = initial;
}
...
}
总结一下Getx状态管理的实现,本质上是观察者模式的实现。ObxWidget创建Observer观察者,Observer收到事件回调时调用setState刷新树,接着在build方法的时候将需要监听的Rx对象的subject添加到Observer的监听,Rx对象在set value或者调用refresh时候会通过subject发送值。Observer在接收到subject的通知之后会调用自身的subject通知ObxWdiget实现刷新视图。