Flutter状态管理--Getx学习1--Obx

4,399 阅读4分钟

Getx状态管理

声明式的界面开发就是如今的潮流,不管你喜不喜欢这样的编码方式,ReactFlutterAndroid Jetpack Compose等都是使用声明式编写的。使用声明式编写的UI界面就是应用的当前状态,对于应用来说时时刻刻知道当前状态很重要,所以状态管理也就至关重要;

相比于Providerflutter_bloc状态管理框架,突破InheritedWidget的限制需要依赖于BuildContext,在没有BuildContext的情况下无法使用;

GetX的官方计数器示例

先看下代码

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() => runApp(GetMaterialApp(home: HomePage()));

class CounterController extends GetxController{
  var count = 0.obs;
  increment() => count++;
  
  @override
  void onInit() {
    super.onInit();
    print("init-----");
  }

  @override
  void onReady() {
    super.onReady();
    print("----onready");
  }

  @override
  void onClose() {
    super.onClose();
    print("----onClose");
  }
}

class HomePage extends StatelessWidget {

  @override
  Widget build(context) {

    // 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
    final CounterController 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));
  }
}

class Other extends StatelessWidget {
  // 你可以让Get找到一个正在被其他页面使用的Controller,并将它返回给你。
  final Controller c = Get.find();

  @override
  Widget build(context){
     // 访问更新后的计数变量
     return Scaffold(body: Center(child: Text("${c.count}")));
  }
}

这是一个简单的项目,但它已经让人明白Get的强大。心里绝对有疑问???怎么做这么简单的状态管理的,通过源码分析解决心中的疑问;

GetX 状态管理、依赖管理分析

  • 状态管理

要想让一个数据变成可观察,你只需要在它的末尾加上".obs"

var count = 0.obs;
  • 依赖管理

Get有一个简单而强大的依赖管理器,它允许你只用1行代码就能检索到与你的Bloc或Controller相同的类,无需Provider上下文,无需 inheritedWidget。

final Controller c = Get.put(Controller());
  • 依赖管理数据怎么存储

通过debug断点CounterController的onInit方法查看调用栈

image.png

调用栈可以看到CounterController数据存到了GetInstance全局单例中,所以与flutter_bloc provider状态管理数据存放到了InheritedWidget中不同,Getx把数据存到了全局单例中,不依赖于UI,将业务逻辑与界面分离,低耦合,更灵活;

Obx widget怎么响应数据变化的

  /// Obx widget 只用这个地方使用到了可观察的count;
  Obx(() => Text("Clicks: ${c.count}"))

  /// 查看count的value;这里的是RxInt类型,c.count.value 简写 c.count;这里面设置了监听
  /// Returns the current [value]

  T get value {
    if (RxInterface.proxy != null) {
      RxInterface.proxy.addListener(subject);
    }
    return _value;
  }

继续查看Obx widget的源码

class Obx extends ObxWidget {
  final WidgetCallback builder;

  const Obx(this.builder);

  @override
  Widget build() => builder();
}

abstract class ObxWidget extends StatefulWidget {
  const ObxWidget({Key key}) : super(key: key);

  @override
  _ObxState createState() => _ObxState();

  @protected
  Widget build();
}

class _ObxState extends State<ObxWidget> {
  RxInterface _observer;
  StreamSubscription subs;

  _ObxState() {
    _observer = RxNotifier();
  }

  @override
  void initState() {
    /// 设置widget刷新监听,数据改变后调用_updateTree
    subs = _observer.listen(_updateTree);
    super.initState();
  }

  void _updateTree(_) {
    if (mounted) {
      setState(() {});
    }
  }

  @override
  void dispose() {
    subs.cancel();
    _observer.close();
    super.dispose();
  }

  Widget get notifyChilds {
    /// 这里可以看到我们设置Rx数据value时候的用到的RxInterface.proxy
    final observer = RxInterface.proxy;
    /// 将_observer赋值给RxInterface.proxy
    RxInterface.proxy = _observer;
    /// 再调用widget.build(),继而调用builder(),执行到回调builder时候这个时候
    /// RxInterface.proxy != null 数据的更新推动UI的更新建立了关系
    final result = widget.build();
    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的值
    RxInterface.proxy = observer;
    return result;
  }

  @override
  Widget build(BuildContext context) => notifyChilds;
}
  • 数据更新响应流程

  /// 再来看获取数据
  T get value {
    /// Obx在build的时候RxInterface.proxy != null  RxInterface.proxy= RxNotifier();
    if (RxInterface.proxy != null) {
      RxInterface.proxy.addListener(subject);
    }
    return _value;
  }
  /// RxNotifier.addListener() 会调用 mixin NotifyManager<T>中addListener的方法
  void addListener(GetStream<T> rxGetx) {
    if (!_subscriptions.containsKey(rxGetx)) {
    /// 这个rxGetx就是Rx响应数据中 GetStream<T> subject = GetStream<T>();
      final subs = rxGetx.listen(subject.add);
      final listSubscriptions =
          _subscriptions[rxGetx] ??= <StreamSubscription>[];
      listSubscriptions.add(subs);
    }
  }
/// Rx响应数据中 GetStream<T> subject设置listen()的回调是RxNotifier中subject的add方法
  void add(T event) {
    assert(!isClosed, 'You cannot add event to closed Stream');
    _value = event;
    _notifyData(event);
  }
   /// 设置数据响应
  set value(T val) {
    if (_value == val && !firstRebuild) return;
    firstRebuild = false;
    _value = val;
    ///  GetStream<T> subject = GetStream<T>();
    subject.add(_value);
  }
/// 查看 subject.add(_value)方法
 void add(T event) {
    assert(!isClosed, 'You cannot add event to closed Stream');
    _value = event;
    _notifyData(event);
  }
 /// 遍历_onData 执行每个订阅数据的_data回调 RxNotifier中subject的add方法
 /// RxNotifier中subject的add方法继续遍历每个订阅对象的_data回调就是_updateTree回调
 void _notifyData(T data) {
    _isBusy = true;
    for (final item in _onData) {
      if (!item.isPaused) {
        item._data?.call(data);
      }
    }
    _isBusy = false;
  }  

  • UI刷新订阅流程

 /// 上面ObxWidget源码的设置UI更新
    @override
  void initState() {
    subs = _observer.listen(_updateTree);
    super.initState();
  }
/// _observer.listen(_updateTree);  会调用 mixin NotifyManager<T>中listen方法
/// subject GetStream类型
   StreamSubscription<T> listen(
    void Function(T) onData, {
    Function onError,
    void Function() onDone,
    bool cancelOnError = false,
  }) =>
      subject.listen(onData,
          onError: onError, onDone: onDone, cancelOnError: cancelOnError);
          
///最后UI刷新监听订阅添加到List<LightSubscription<T>> _onData = <LightSubscription<T>>[];

 FutureOr<void> addSubscription(LightSubscription<T> subs) async {
    if (!_isBusy) {
      return _onData.add(subs);
    } else {
      await Future.delayed(Duration.zero);
      return _onData.add(subs);
    }
  }

断点查看当数据更改的时候先遍历_onData的订阅_data回调,RxNotifier中subject的add方法

image.png RxNotifier中subject的add方法继续触发遍历_onData的订阅_data回调 _updateTree()方法; image.png

Getx功能很多,更多查看官方文档github网速限制可以查看码云gitee