当前位置: 首页>后端>正文

9. Flutte3.0遥遥领先系列-一文教你完全掌握getX, 手写getX框架

目录:
1.优点
2.优点分析: GetX怎么将逻辑层和界面层解耦的
3.优点分析: GetX怎么实现状态管理的 Obx的基本原理是什么? 局部刷新原理 obx和obs?
4.优点分析: binding基本原理是什么?
5.优点分析: 路由管理基本原理是什么?
6.getx的缺点是啥?
7.手写getX

1. GetX相关优势

1.1 )依赖注入

GetX是通过依赖注入的方式,存储相应的XxxGetxController;已经脱离了InheritedWidget那一套玩法,自己手动去管理这些实例,使用场景被大大拓展
简单的思路,却能产生深远的影响:优雅的跨页面功能便是基于这种设计而实现的、获取实例无需BuildContext、GetBuilder自动化的处理及其减少了入参等等

1.2 )跨页面交互的状态管理

这绝对是GetX的一个优点!对于复杂的生产环境,跨页面交互的场景,实在太常见了,GetX的跨页面交互,实现的也较为优雅

1.3 )路由管理

getx内部实现了路由管理,而且用起来,非常简单!bloc没实现路由管理,我不得不找一个star量高的路由框架,就选择了fluro,但是不得不吐槽下,fluro用起来真的很折磨人,每次新建一个页面,最让我抗拒的就是去写fluro路由代码,横跨几个文件来回写,头皮发麻
GetX实现了动态路由传参,也就是说直接在命名路由上拼参数,然后能拿到这些拼在路由上的参数,也就是说用flutter写H5,直接能通过Url传值,OMG!可以无脑舍弃复杂的fluro了

1.4 ) 实现了全局BuildContext
1.5 )国际化,主题实现
生命周期

用了Getx的state管理之后, 你再也用不着StatefulWidget了. 仅仅StatelessWidget就够你用了! 性能自然也提升很多!

2. GetX怎么将逻辑层和界面层解耦的

此处需要划分三个结构了:state(状态层),logic(逻辑层),view(界面层)

为什么写成这样三个模块,需要把State单独提出来,为了复杂的业务, 显的更简单!

举例:

之前的写法

class IdentificationCard extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return IdentificationState();
  }
}

class IdentificationState extends State<IdentificationCard> {
  String date = "555";
  String name = "666";

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
                Text('date : $date'),
                Text('name : $name'), 
                GestureDetector(onTap: () {
                  setState(() {
                    date = "777";
                  });
                }, child: const Text('修改'))],
    );
  }
}

用getx的实现
新建一个类, 定义一个状态, GetxController
添加一个Obx就能自动监听状态的改变并且刷新UI了

/// 状态
class IdentificationState {
  RxString date = "555".obs;
  RxString name = "666".obs;
}

/// 业务逻辑
class IdentificationController extends GetxController {
  IdentificationState state = IdentificationState();
}

/// 展示
class IdentificationCard extends StatelessWidget {
  IdentificationController controller = Get.put(IdentificationController());

  @override
  Widget build(BuildContext context) {
    return Obx(() {
      return Column(
        children: [
          Text('date : ${controller.state.date}'),
          Text('name : ${controller.state.name}'),
          GestureDetector(
              onTap: () {
                controller.state.date.value = "777";
              },
              child: const Text('修改'))
        ],
      );
    });
  }
}

3.GetX实现状态管理

GetX 的刷新方案分为手动刷新与自动刷新,GetBuilder 与 Obx ,分别对应范围刷新与局部刷新

2者的区别:

Obx 响应式状态管理

Obx 可以配合响应式字段局部的精准刷新避免父容器无效重构,缺点是字段变为响应式的Rx包装类,布局也需要被Obx包裹了,破坏了原生代码观赏性。

GetBuilder 状态管理器

GetBuilder 就是指定区域范围手动去刷新的,可以分区设置多个刷新区域,可选择单个控件或容器,在一些特定场景下有奇效,但是如果不理解滥用一样会导致性能问题。

3.1 Obx 的基本原理是什么?

Obx是配合Rx响应式变量使用
这样一来我们就明白了Obx实际上是一个StatefulWidget,它里面监听了一个GetStream,一旦GetStream有事件通知,它就会进行setState重新进行Widget的构造.
GetBuilder 与 Obx 两者结合,一个指定区域范围手动刷新,一个是局部控件点对点刷新

var build = () => Text(name.value)

Obx(build);

源码: Obx继承了一个抽象ObxWidget类,将传递进来的build方法给了ObxWidget

class Obx extends ObxWidget {
  final WidgetCallback builder;

  const Obx(this.builder);

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

ObxWidget继承了有状态组件,并且build函数让Obx类实现了

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

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

  @protected
  Widget build();
}
对GexX的状态管理做一个简单总结,

基于Obx收集依赖状态, 实际一个StatefulWidget,它的State也就是ObxState中监听了GetStream事件流,通过接收GetStream事件流调用setState重新构建Obx,Rx对象在改变value的时候会向GetStream事件流发送事件,这样就会导致Obx进行刷新了.

9. Flutte3.0遥遥领先系列-一文教你完全掌握getX, 手写getX框架,第1张
Rx原理.jpg
3.2 GetBuilder

GetBuilder 是一个 Widget 组件, 在 GetX 的状态管理中,GetBuilder 的主要作用是结合 GetxController 实现界面数据的更新
demo使用

class CounterBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => CounterController());
  }
}


class CounterController extends GetxController {
  int count = 0;
  
  void increase(){
    count += 1;
    update();
  }
}

class CounterPage extends StatelessWidget {

  final controller = Get.find<CounterController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Counter"),
      ),
      body: Center(
        child: GetBuilder<CounterController>(builder: (logic) {
          return Text("${controller.count}", style: const TextStyle(fontSize: 50),);
        }),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: controller.increase,
      ),
    );
  }
}

demo调用总结: 然后调用 update 方法更新界面数据,从而实现计数器的功能。
状态管理源码分析:

class GetBuilder<T extends GetxController> extends StatefulWidget {
  final GetControllerBuilder<T> builder;
  final bool global;
  final Objectid;
  final Stringtag;
  final bool autoRemove;
  final bool assignId;
  final Object Function(T value)filter;
  final void Function(GetBuilderState<T> state)initState,
      dispose,
      didChangeDependencies;
  final void Function(GetBuilder oldWidget, GetBuilderState<T> state)?
      didUpdateWidget;
  final Tinit;

  const GetBuilder({
    Keykey,
    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,
  }) : super(key: key);

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

GetBuilder 是继承自 StatefulWidget

GetBuilder 就是指定区域范围手动去刷新的,可以分区设置多个刷新区域,可选择单个控件或容器,在一些特定场景下有奇效,但是如果不理解滥用一样会导致性能问题。

GetBuilderState

class GetBuilderState<T extends GetxController> extends State<GetBuilder<T>>
    with GetStateUpdaterMixin {
  Tcontroller;
  bool_isCreator = false;
  VoidCallback_remove;
  Object_filter;

  @override
  void initState() {...}

  void _subscribeToController() {...}

  void _filterUpdate() {...}

  @override
  void dispose() {...}

  @override
  void didChangeDependencies() {...}

  @override
  void didUpdateWidget(GetBuilder oldWidget) {...}

  @override
  Widget build(BuildContext context) {...}
}

方法: build()

@override
Widget build(BuildContext context) {
  return widget.builder(controller!);
}
9. Flutte3.0遥遥领先系列-一文教你完全掌握getX, 手写getX框架,第2张
getbuild.jpg

通过对 GetBuilder 的源码分析,基本了解了 GetBuilder 各个参数的作用和实现原理。

GetBuilder 参数作用总结如下:

builder: Widget 构建器,创建界面显示的 Widget
init: 初始化 Controller 值,当 global 为 false 时使用该值作为 Controller,当 global 为 true 时且 Controller 未注册依赖,则将 init 的值注入依赖使用。
global: 是否全局,作用于 Controller 初始化中,与 init 结合使用
autoRemove: 是否自动移除 Controller 依赖,结合 assignId 一起使用
assignId: 为 true 时结合 autoRemove 使用会自动移除 Controller 依赖关系
filter: 过滤器,通过返回值过滤是否需要刷新,返回值变化时才会刷新界面
tag: Controller 依赖注入的 tag,根据 tag 获取 Controller 实例
id: 刷新标识,结合 Controller 的 update 使用,可以刷新指定 GetBuilder 控件内的 Widget
initState: 回调函数,生命周期 initState 方法中调用
dispose: 回调函数,生命周期 dispose 中调用
didUpdateWidget: 回调函数,生命周期 didUpdateWidget 中调用
didChangeDependencies: 回调函数,生命周期 didChangeDependencies 中调用

4. 依赖管理: Binding:

依赖注入: 就是赋值, 但是很多类给你的类赋值, 这样就很乱了
Binding的使用: 一般和controller在一起使用
binding模块需要在getx路由页面进行绑定;进入页面的时候,统一懒注入binding模块的GetXController

9. Flutte3.0遥遥领先系列-一文教你完全掌握getX, 手写getX框架,第3张
依赖注入.jpg
class _GetImpl extends GetInterface {}

final Get = _GetImpl();

extension Inst on GetInterface {
  S put<S>(S dependency,
          {Stringtag,
          bool permanent = false,
          InstanceBuilderCallback<S>builder}) =>
      GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
}
class GetInstance {
  factory GetInstance() => _getInstance = GetInstance._();

  const GetInstance._();

  static GetInstance_getInstance;

  static final Map<String, _InstanceBuilderFactory> _singl = {};

  S put<S>(
    S dependency, {
    Stringtag,
    bool permanent = false,
    @deprecated InstanceBuilderCallback<S>builder,
  }) {
    _insert(
        isSingleton: true,
        name: tag,
        permanent: permanent,
        builder: builder ?(() => dependency));
    return find<S>(tag: tag);
  }

  void _insert<S>({
    boolisSingleton,
    Stringname,
    bool permanent = false,
    required InstanceBuilderCallback<S> builder,
    bool fenix = false,
  }) {
    final key = _getKey(S, name);
    _singl.putIfAbsent(
      key,
      () => _InstanceBuilderFactory<S>(
        isSingleton,
        builder,
        permanent,
        false,
        fenix,
        name,
      ),
    );
  }

  String _getKey(Type type, Stringname) {
    return name == null type.toString() : type.toString() + name;
  }

  S find<S>({Stringtag}) {
    final key = _getKey(S, tag);
    if (isRegistered<S>(tag: tag)) {
      if (_singl[key] == null) {
        if (tag == null) {
          throw 'Class "$S" is not registered';
        } else {
          throw 'Class "$S" with tag "$tag" is not registered';
        }
      }
      final i = _initDependencies<S>(name: tag);
      return i ?_singl[key]!.getDependency() as S;
    } else {
      // ignore: lines_longer_than_80_chars
      throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"';
    }
  }
}

5. 路由管理之命名路由

特点:封装了context, 封装了拦截器!
路由管理之简单路由

GetMaterialApp(
    unknownRoute: GetPage(name: '/notfound', page: () => UnknownRoutePage()),
    routingCallback: (routing) {
      if(routing?.current == '/second'){
       ///处理一些业务
      }
    },
    initialRoute: '/',
    getPages: [
      GetPage(name: '/first', page: ()=>First()),
      GetPage(name: '/second', page: ()=>Second())
    ],
  )
9. Flutte3.0遥遥领先系列-一文教你完全掌握getX, 手写getX框架,第4张
路由管理.jpg
问题: 为何不用Flutter自己的Router系统?

使用时还需要有一个context实例. 但我们并不是随时随地都持有一个context的, 这也局限了我们的使用场景.

6. GETX的缺点:

第一个缺点

Get.to(widgetObj, bindings)是可以注入binding.

但是Get.toNamed()并不支持binding参数啊. 我的跳转一般都是用toNamed的, 所以注定了这种方式我用不了.

第二个缺点

这个缺点很隐藏, 很容易出问题. 以上面的binding为例

HomeBinding中提供了 HomeController, Service 两个对象
DetailsBinding中提供了 DetailsController 对象 但其实我们的Details页中也会用到Service对象.
之所以不出现"details页中说找不到Service"的crash, 是因为用户先打开的home页, Home已经往Get中写入了Service对象了, 所以等之后打开detail页时, serivce对象已经有了, 能够Get.find()得到, 所以不会有NPE错误.

但要是deep link的场景呢

: 你直接跳到了Detail页, 结果就因为没有经过home页, 所以Service service = Get.find()找不到service对象, 应用会crash.

所以现在就明白了, 第二个缺点就是: 上面两个Binding有隐藏的依赖性 DetailsBinding其实依赖于HomeBinding. HomeBinding不先放好service, 那DetailsBinding提供不了Serivce, 就可能会让Detail页crash.

第三个缺点: obs会频繁刷新;

7. 手写getX

3大核心功能
7.1 依赖注入

///依赖注入,外部可将实例,注入该类中,由该类管理
class Easy {
  ///注入实例
  static T put<T>(T dependency, {Stringtag}) =>
      _EasyInstance().put(dependency, tag: tag);

  ///获取注入的实例
  static T find<T>({Stringtag, Stringkey}) =>
      _EasyInstance().find<T>(tag: tag, key: key);

  ///删除实例
  static bool delete<T>({Stringtag, Stringkey}) =>
      _EasyInstance().delete<T>(tag: tag, key: key);
}

///具体逻辑
class _EasyInstance {
  factory _EasyInstance() => _instance = _EasyInstance._();

  static _EasyInstance_instance;

  _EasyInstance._();

  static final Map<String, _InstanceInfo> _single = {};

  ///注入实例
  T put<T>(T dependency, {Stringtag}) {
    final key = _getKey(T, tag);
    //只保存第一次注入:针对自动刷新机制优化,每次热重载的时候,数据不会重置
    _single.putIfAbsent(key, () => _InstanceInfo<T>(dependency));
    return find<T>(tag: tag);
  }

  ///获取注入的实例
  T find<T>({Stringtag, Stringkey}) {
    final newKey = key ?_getKey(T, tag);
    var info = _single[newKey];

    if (info?.value != null) {
      return info!.value;
    } else {
      throw '"$T" not found. You need to call "Easy.put($T())""';
    }
  }

  ///删除实例
  bool delete<T>({Stringtag, Stringkey}) {
    final newKey = key ?_getKey(T, tag);
    if (!_single.containsKey(newKey)) {
      print('Instance "$newKey" already removed.');
      return false;
    }

    _single.remove(newKey);
    print('Instance "$newKey" deleted.');
    return true;
  }

  String _getKey(Type type, Stringname) {
    return name == null type.toString() : type.toString() + name;
  }
}

class _InstanceInfo<T> {
  _InstanceInfo(this.value);

  T value;
}

7.2 状态管理

///自定义个监听触发类
class EasyXNotifier {
  List<VoidCallback> _listeners = [];

  void addListener(VoidCallback listener) {
    _listeners.add(listener);
  }

  void removeListener(VoidCallback listener) {
    for (final entry in _listeners) {
      if (entry == listener) {
        _listeners.remove(entry);
        return;
      }
    }
  }

  void dispose() {
    _listeners.clear();
  }

  void notify() {
    if (_listeners.isEmpty) return;

    for (final entry in _listeners) {
      try {
        entry.call();
      } catch (e) {
        print(e.toString());
      }
    }
  }
}

7.3 路由管理

///刷新控件,自带回收机制
class EasyBuilder<T extends EasyXController> extends StatefulWidget {
  final Widget Function(T logic) builder;

  final Stringtag;
  final bool autoRemove;

  const EasyBuilder({
    Keykey,
    required this.builder,
    this.autoRemove = true,
    this.tag,
  }) : super(key: key);

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

class _EasyBuilderState<T extends EasyXController>
    extends State<EasyBuilder<T>> {
  late T controller;

  @override
  void initState() {
    super.initState();

    controller = Easy.find<T>(tag: widget.tag);
    controller.xNotifier.addListener(() {
      if (mounted) setState(() {});
    });
  }

  @override
  void dispose() {
    if (widget.autoRemove) {
      Easy.delete<T>(tag: widget.tag);
    }
    controller.xNotifier.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(controller);
  }
}


https://www.xamrdz.com/backend/39k1936112.html

相关文章: