一起源码 之 GetX(三): GetBuilder

4,007 阅读3分钟

往期列表:

在上一节中我们一块学习了GetX中的GetxController的源码和作用,以及GetX的详细解读。

在本文中,我们继续学习官方demo中的另一个批量更新控件:GetBuilder,代码如下:

class GetBuilder<T extends GetxController> extends StatefulWidget {
    final GetControllerBuilder<T> builder;
    final bool global;
    final Object id;
    final String tag;
    final bool autoRemove;
    final bool assignId;
    final Object Function(T value) filter;
    final void Function(State state) initState, dispose, didChangeDependencies;
    final void Function(GetBuilder oldWidget, State state) didUpdateWidget;
    final T init;

const GetBuilder({
    Key key,
    this.init,
    this.global = true,
    @required this.builder,
    this.autoRemove = true,
    this.assignId = false,
    this.initState,
    this.filter,
    this.tag,
    this.dispose,
    this.id,
    this.didChangeDependencies,
    this.didUpdateWidget,
})  : assert(builder != null),
        super(key: key);

    @override
    _GetBuilderState<T> createState() => _GetBuilderState<T>();
}

从上述源码可以看出,GetBuilder的定义和GetX的定义十分相近,每个参数所提供的功能也基本相同。他们的不同之处在于:

  • 定义规定的泛型不同
    • class GetBuilder<T extends GetxController> 我们知道GetX规定的入参泛型为 DisposableInterface 类型,所以GetX的入参只支持生命周期回调,而不支持controller手动更新。我们在分析GetxController的源码时介绍过,他继承自DisposableInterface,并且又通过mixin的方式融入了批量更新的功能。所以我们推测GetBuilder支持controller中手动更新
  • filter参数
  • id参数

下面我们具体看一下State的实现,寻找三处不同的具体差别。

1. _GetBuilderState源码分析

我们直接进入正题,查看一下_GetBuilderState的源码:

class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> with GetStateUpdaterMixin {
  T controller;
  bool isCreator = false;
  VoidCallback remove;
  Object _filter;

  @override
  void initState() {
    super.initState();
    // 初始化controller,与GetX中的一致,省略...
    
    // 1
    if (widget.filter != null) {
      _filter = widget.filter(controller);
    }

    _subscribeToController();
  }

  // 2
  void _subscribeToController() {
    remove?.call();
    remove = (widget.id == null)
        ? controller?.addListener(
            _filter != null ? _filterUpdate : getUpdate,
          )
        : controller?.addListenerId(
            widget.id,
            _filter != null ? _filterUpdate : getUpdate,
          );
  }

  // 3
  void _filterUpdate() {
    var newFilter = widget.filter(controller);
    if (newFilter != _filter) {
      _filter = newFilter;
      getUpdate();
    }
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(controller);
  }
}
mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
  void getUpdate() {
    if (mounted) setState(() {});
  }
}

源码中,我们省略了与GetX源码基本相同的部分。在GetBuilder的init方法中,也进行了controller的初始化,也对widget中的dispose等方法回调做了转发处理。下面我们主要看一下GetBuilder与GetX中的区别,从上到下依次为:

  • mixin GetStateUpdaterMixin 从GetStateUpdaterMixin中的代码我们知道,其实并没有做太多事情,只是增加了state状态判断以及简化了state的调用。
  • listener的注册 当init执行完成后,会根据当前widget.id是否为null,在controller中注册更新监听。而监听的回调会根据filter的状态,决定是否更新UI。例如,我们的controller是多个GetBuilder共享,当controller更新时,可使用refreshGroup()的方式批量更新对应id的GetBuilder,使用refresh()的方式更新没有指定id的GetBuilder。
  • filter 根据注释1我们知道,当init的时候会第一次调用filter。
    • 如果返回值不为空,则后续的更新回调,将使用**_filterUpdate()**的方式进行。而此方法中,又会回调filter,让外部决定是否更新。如果新返回的值与原始值相同,则不更新,否则更新。
    • 如果返回值为空,则直接使用mixin进来的getUpdate方法进行更新
  • build方法 从源码可以看到,这里并没有像GetX与Obx中那样使用proxy交换的方式远程注册监听,而是直接调用的widget.builder(controller)。由此代码以及listener代码我们可知,GetBuilder不支持自动更新UI,需要配合controller中的refresh、refreshGroup进行UI更新。

2. 总结

通过上面的源码阅读与分析我们知道,GetBuilder的设计是用来与GetxController进行配合使用。在需要批量更新和区别更新(refreshGroup)时,会比GetX和Obx更加灵活。

GetBuilder与GetX等不同,他不支持自动更新。需要我们在Controller中有数据变化和更新需求时,手动的进行更新。

GetBuilder提供了filter参数,通过此参数,我们可以更加细粒度的控制每次更新触发时,是否真正的执行更新,或者过滤掉更新请求。