前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter Getx状态管理源码解析

Flutter Getx状态管理源码解析

作者头像
用户4458175
发布2022-04-20 09:35:11
1.1K0
发布2022-04-20 09:35:11
举报
文章被收录于专栏:andy的小窝

GetX 是 Flutter 上的一个轻量且强大的解决方案:高性能的状态管理、智能的依赖注入和便捷的路由管理。本文来解析下Getx是怎样实现的状态管理。老规矩上Counter Demo。

代码语言:javascript
复制
class Controller extends GetxController{
  var count = 0.obs;
  increment() => count++;
}
代码语言:javascript
复制
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方法。

代码语言:javascript
复制
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关联起来的。

代码语言:javascript
复制
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;

代码语言:javascript
复制
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是抽象类。应该有小伙伴猜到了吧。实际上就是RxNotifierNotifyManager实现了RxInterface的抽象方法。可以看到NotifyManager新建了GetStream(Get自己实现的Stream类) subject和一个_subscriptions

代码语言:javascript
复制
class RxNotifier<T> = RxInterface<T> with NotifyManager<T>;
代码语言:javascript
复制
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();
  }
}

目前为止一切都还没有关联,关键来了,我们在Controller0.obs实际是Getx对int的扩展(extension),obs方法会返回一个Rx对象(Getx的拓展包括Stringintdoublebool任意类型TListMapSet…具体可参照文档或get_rx包)。

Rx类,Rx是一个抽象类继承自_RxImpl_RxImpl同样是抽象类,可以看到_RxImpl同样是继承RxNotifier,不同的是mixin进RxObjectMixinRxObjectMixin是对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方法,从而关联刷新。

代码语言:javascript
复制
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实现刷新视图。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档