Scoped 、 Provider 与 Get的状态管理

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, 在下一帧触发对应的ElementperformRebuild(), 在performRebuild()中调用built = build(), 而build()函数如下:

Widget build(BuildContext context) => builder(context);

调用了我们使用 Provider 时传入的builder.

3. Get

不管是 Scoped 还是 Provider, 都是使用InheritedWidget持有数据, InheritedWidget是通过每个Element都持有一个_ineritedWidgetsmap 在整个 APP 内同步数据, 或许我们可以自己实现一套机制, 需要满足以下条件.

  1. 通过少量条件即可在期望范围内的任何地方获得数据的持有者或者数据本身
  2. 数据改变后可以通知到 UI 进行改变

如果想要实现任何地方都能获得数据, 无疑使用单利或者全局的 map 比较好, 而需要数据修改后通知UI进行改变, 则Publisher-Subscriber模式也是一个优秀的选择.

很巧, Get的GetBuilder 正好符合以上期望.

Get 使用 GetInstance_singl持有数据模型,

// GetInstance
static final Map _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 {
  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 subject = GetStream(), 我们其实是对subject做了监听, 当subject.add(event)被调用时就会触发监听.

b. _observerRxObject的监听.

作为响应式状态管理方案, 响应式对象, 即我们想要监听的数据可称为RxObject. 下面代码中count就是一个RxObject.

var count = 0.obs;

作为一个响应式方案, GetX中数据源也是RxNotifier, 但RxNotifier不一定是数据源, 这样应该是为了组成响应链.
但如果一个RxNotifier作为数据源, 那就需要具备一些独有的能力, 在GetX中以_RxImplRxObjectMixin实现的, 其中_RxImpl及一些列子类用来实现数据存储和计算功能.
RxObjectMixin则主要实现数据源的事件触发的相关功能能, 所以GetX的事件都来自RxObjectMixin.
这里将所有 with RxObjectMixin 的对象称为RxObject.

_observer不是RxObject, 它即不持有数据, 也不会发出事件, GetX_observerRxObject做了监听, 实现在_observer.addListener()方法中, 由RxObject主动调起.

// RxNotifier
class RxNotifier = RxInterface with NotifyManager;

mixin NotifyManager {
  GetStream subject = GetStream();
  final _subscriptions = >{};

  bool get canUpdate => _subscriptions.isNotEmpty;

  // RxObject调用这个方法, 这里实现了_oberver对RxObject的监听
  void addListener(GetStream rxGetx) {
    if (!_subscriptions.containsKey(rxGetx)) {
      final subs = rxGetx.listen((data) {
        if (!subject.isClosed) subject.add(data);
      });
      final listSubscriptions =
          _subscriptions[rxGetx] ??= [];
      listSubscriptions.add(subs);
    }
  }

  // Obx调用这个方法, 监听_observer.
  StreamSubscription 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(RxNotifier observer, ValueGetter 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()这行代码中, 调用了我们传入Obxbuilder, 一般builder可以这样写:

Obx(() {
  return Text('count: $count');
})

这最终调用了RxObject.valueget方法.

定义一个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), 最终完成了_observerRxObject的监听.

c. 发送事件

通过 a 和 b 两步, 已经完成了整个监听链条, 最终则是触发事件, 或者说发送事件.

RxInt 为例, 我们如果监听一个int类型的数据, 需要写为var num = 0.obs, obs函数返回的就是一个RxInt对象(集成自RxObject), 其内部实现了int类型的运算, 下面是加法运算:

RxInt operator +(int other) {
  value = value + other;
  return this;
}

其中value = value + othervalue做了赋值, 毫无疑问会触发RxObject.valueset方法, 当我们调用num++, 最终会在set方法中调用subject.add(_value), 完成了事件发送.

监听链:


getx.png

你可能感兴趣的:(Scoped 、 Provider 与 Get的状态管理)