目录:
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进行刷新了.
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!);
}
通过对 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
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())
],
)
问题: 为何不用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);
}
}