• preface
  • GetBuilder manual management
    • core idea
    • How do views and Controllers establish their relationship
      • Analysis of theGetBuilderThe source of
      • Analysis of theGetxControllerThe source of
    • conclusion
  • GetX responsive automatic management
    • core idea
    • Analyze GetX source code
    • conclusion
  • GetBuilder vs GetX

preface

I recently wrote a new project that uses state management. However, BLOC was selected as the state management before, and the effect was quite good. UI and logic are separated, but if you look back at the project, you will find that there is too much template code for various state classes and event classes. GetX’s API is simple enough and intuitive enough, but the authors say it works just fine.

Since the implementation of the simple, of course, to see its implementation principle

  • GetBuilder manages state manually
  • GetX responsive state management

GetBuilder manual management

Let’s start with a simple example of creating the Controller and View classes. The Controller calls update() to refresh the UI in the View layer.

class Controller extends GetxController {
  int counter = 0;
  void increment() {
    counter++;
    update(); // When increments are called, update() is used to update counter variables on the user interface.
  }
}

GetBuilder<Controller>(
  init: Controller(), 
  builder: (_) => Text(
    '${_.counter}',),Copy the code

core idea

  1. Define a class that inherits GetxController: the logic-Controller layer
  2. Define a GetBuilder class: interface – *View layer
  3. Controlelr calls Update () to refresh the UI

How do views and Controllers establish their relationship

Analysis of theGetBuilderThe source of

  1. It is a StatefulWidget
  2. InitState () finds the defined Controller. There are some search strategies that are irrelevant to us and we ignore them
  3. SubscribeToController () Cool found! You are ready to start the relationship by adding a listener to the Controller by calling addListner() and binding the function getUpdate
  4. GetUpdate comes from GetStateUpdaterMixin. The function is simple: setState() is executed when the current widget is alive.
/ / get - 3.22.2 / lib/get_state_manager/SRC/simple/get_state dart
class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  finalT init; .@override
  _GetBuilderState<T> createState() => _GetBuilderState<T>();

}

class _GetBuilderState<T extends GetxController> extends State<GetBuilder<T>> with GetStateUpdaterMixin {
  T controller;

  @override
  void initState() {
    / / for the Controller./ / subscribe
    _subscribeToController();
  }

  void_subscribeToController() { remove? .call(); remove = (widget.id ==null)
        ? controller?.addListener(getUpdate)
        : controller?.addListenerId(widget.id, getUpdate);
  }


}
mixin GetStateUpdaterMixin<T extends StatefulWidget> on State<T> {
  void getUpdate() {
    if(mounted) setState(() {}); }}Copy the code

Analysis of theGetxControllerThe source of

  1. GetxController implements the Listenable listener protocol
  2. AddListener is recorded in a List object called updater
  3. Call Update () -> refresh() to iterate over all listeners and call.
 class GetxController extends DisposableInterface with ListNotifier {
     void update([List<String> ids, bool condition = true]) {
     // Remove useless code. refresh(); . }}class ListNotifier implements Listenable {
     List<GetStateUpdate> _updaters = <GetStateUpdate>[];

     @protected
     void refresh() {
     for (var element in_updaters) { element(); }}@override
     Disposer addListener(GetStateUpdate listener) {
     _updaters.add(listener);
     return() => _updaters.remove(listener); }}Copy the code
  1. GetBuilder is a state class that establishes a relationship with Controller and adds the current View as listener to initState().
  2. The Controller maintains a list of updaters for each listener and updates all views with updates ().

conclusion

  • Advantages: The API is simple and clear, and takes up very little memory.
  • Downside: Still need to manually update(), is there a responsive interface? It allows us to call it directly. Of course it does. Look down.

GetX responsive automatic management

We also need to declare the Controller and View classes, the most intuitive feeling is that there is no manual update() in the Controller, great!!

class Controller extends GetXcontroller {
  final count1 = 0.obs;
  final count2 = 0.obs;
  int get sum => count1.value + count2.value;
}

GetX<Controller>(
  builder: (controller) {
    print("count 1 rebuild");
    return Text('${controller.count1.value}'); },),Copy the code

core idea

  1. Define a Controller class: the logic-Controller layer

    The Controller contains multiple variables in the form of.obs, which we call responsive variables

  2. Define a GetBuilder class: the interface-View layer

  3. Whenever the responsive variable changes, the View will be automatically notified to refresh the screen, wow amazing 🙂

Analyze GetX source code

  1. GetX is a StatefulWidget

    class GetX<T extends DisposableInterface> extends StatefulWidget {
      final GetXControllerBuilder<T> builder;
    
      const GetX({
        this.builder,
      });
    
      @override
      GetXState<T> createState() => GetXState<T>();
    }
    Copy the code
  2. When the GetState class initializes, it creates a _observer = RxNotifier(). RxNotifier() contains a Stream

    1. InitState () _observer starts adding a listener to refresh the View

      class GetXState<T extends DisposableInterface> extends State<GetX<T>> {
        GetXState() {
        _observer = RxNotifier();
        }
        RxInterface _observer;
        StreamSubscription subs;
      
        @override
        void initState() {
        / / for the controller
        // ... 
        subs = _observer.listen((data) => setState(() {}));
        super.initState(); }}Copy the code
    2. Build () is interesting here. RxInterface. Proxy (controller) {obs. Value (obS. Value);

      Widget get notifyChildren {
        final observer = RxInterface.proxy;
        RxInterface.proxy = _observer;
        final result = widget.builder(controller);
        if(! _observer.canUpdate) {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;
      }
      
      @override
      Widget build(BuildContext context) => notifyChildren;
      
      Copy the code
    3. The execution Builder (Controller) contains properties that call obs.value. Start subscribing to changes in the current property and automatically trigger setState() when they happen. This is the core of GetX;

      mixin RxObjectMixin<T> on NotifyManager<T> {
        // controller.count.value
        T get value {
          if(RxInterface.proxy ! =null) {
            RxInterface.proxy.addListener(subject);
          }
          return_value; }}mixin NotifyManager<T> {
        GetStream<T> subject = GetStream<T>();
        final _subscriptions = <StreamSubscription>[];
      
        void addListener(GetStream<T> rxGetx) {
          if (_subscriptions.contains(rxGetx.listen)) {
            return;
          }
      
          finalsubs = rxGetx.listen((data) { subject.add(data); }); _subscriptions.add(subs); }}Copy the code

conclusion

When GetX is initialized, it creates a Stream that listens for changes to the current View, and when obs.value is loaded in the UI, it mounts the View refresh to the OBS property. The View is notified to refresh the page whenever the property changes.

GetBuilder vs GetX

way Automatic update Memory footprint
GetBuilder no There is very little
GetX is general