以下内容针对get 5.0版本,可能和历史版本略有不同
getx 状态管理 架构图
GetInstance 是一个单利, 维护一个控制器字典map< key:controller >用来保存、创建、查找、删除控制器
GetxController 主要是通过 ListNotifierSingleMixin来维护一个函数列表(_updaters),一般存的是widget的setState或者markNeedsBuild, 用来控制 widget重建.
view 通过自定义widget 即重写createEmelent, 并给自定义视图添加getUpate(), getUpdate()最后会调用markNeedsBuild以此来重建widget。
另外get提供了几种自定义StatelessWidget:
-
GetView只是简单添加一个查找控制器的方法, 也最为常用 -
Bind, 在自定义Emelent的生命周期中完成控制器的创建、插入及销毁 -
Binds, 控制器与widget建立绑定关系,与Bind类似,使用可参考GetMaterialAppclass GetMaterialApp extends StatelessWidget { @override Widget build(BuildContext context) => Binds(binds: [ Bind.lazyPut<GetMaterialController>( () => GetMaterialController],child: widget); } -
obx, 通过Notifer向控制器中插入刷新的回调函数(markNeedsBuild), 主要用来局部刷新。 -
GetBuilder对statefullwidget生命周期做一些hooks,也极为常用 -
GetWidget, 解决多个重复页面对应一个控制器的问题, 比如导航中同时存在多个详情页,每个控制器都要有单独的生命周期
StateMixin
StateMixin 就是对ListNotifier的一个简单应用, 开发中会经常用到。
value实现它的get/set方法, 由Notifer添加监听者(通常为view),set方法执行刷新_status与value类似futurize处理异步任务, 一般为网络请求
mixin StateMixin<T> on ListNotifier {
GetStatus<T>? _status;
GetStatus<T> get status {
reportRead();
return _status ??= _status = GetStatus.loading();
}
set status(GetStatus<T> newStatus) {
if (newStatus == status) return;
_status = newStatus;
if (newStatus is SuccessStatus<T>) {
_value = newStatus.data!;
}
refresh();
}
T get state => value;
T? _value;
@protected
T get value {
reportRead();
return _value as T;
}
@protected
set value(T newValue) {
if (_value == newValue) return;
_value = newValue;
refresh();
}
void futurize(Future<T> Function() body,
{T? initialData, String? errorMessage, bool useEmpty = true}) {
final compute = body;
_value ??= initialData;
compute().then((newValue) {
if ((newValue == null || newValue._isEmpty()) && useEmpty) {
status = GetStatus<T>.loading();
} else {
status = GetStatus<T>.success(newValue);
}
refresh();
}, onError: (err) {
status = GetStatus.error(errorMessage ?? err.toString());
refresh();
});
}
}
RouterOutlet是怎么刷新的?
GetDelegate 实现了 ListNotifierSingleMixin 接口, 因此在push / back 等出栈入栈 调用refresh() 可以触发build重建, 不过此时 GetDelegate显然还不满足refresh的条件, 即没有向链表中添加markNeedsBuild函数
向链表中添加markNeedsBuild的两种方式
RouterOutletState 在didChangeDependencies的时候为 routerDelegate 添加了一个setState(() {})函数, 从而使routerDelegate 在出入栈时触发重建
class RouterOutlet<TDelegate extends RouterDelegate<T>, T extends Object>
extends StatefulWidget {
final TDelegate routerDelegate;
// 与例子无关, 忽略实现
@override
_RouterOutletState<TDelegate, T> createState() =>
_RouterOutletState<TDelegate, T>();
}
class _RouterOutletState<TDelegate extends RouterDelegate<T>, T extends Object>
extends State<RouterOutlet<TDelegate, T>> {
RouterDelegate? delegate;
// 与例子无关, 忽略实现
void _listener() {
setState(() {});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 与例子无关, 忽略实现
delegate?.addListener(_listener);
}
}
rx obs 原理解析
-
MiniStream<T>为 get 封装的一个简易版的 stream , 功能和 stream 类似. 性能上比flutter的stream 差许多, 因此 MiniStream 只是用来做参考.-
MiniStream<T>主要作用是对T value添加监听者, 并在合适时机触发MiniSubscription<T>对应的方法 -
MiniSubscription为监听者,on data值发生变化 ,on error发生错误,on done完成. -
T value,实际是重写get,set方法,set方法触发MiniSubscription<T>.onData,value值发生变化 -
listen, 实际上是给value 添加监听者, 监听者对应FastList<T>链表的节点, 一个监听者对应一个Node节点 -
add触发on data -
addError触发on error -
close触发on done
-
-
rx 的核心类为
GetListenable,T value监听部分由StreamController完成.ListNotifierSingleupdater 更新者, 一般用来页面重建T value,同样是重写get,set方法, 不过此时由于监听部分已经由StreamController完成, 因此get,set方法调用了ListNotifierSingle部分方法Rx<T>作用和MiniStream<T>基本类似
-
value.obs明显就是value转成MiniStream/Rx<T>, 从而完成对value的监听操作extension RxT<T> on T { Rx<T> get obs => Rx<T>(this); } -
rx obs 和 obx 通信
-
obx带有getUpdate方法的StatelessWidget,并通过Notifier.append把自身的getUpate()刷新方法封装到NotifierData中(待插入). -
obs即Rx<T>对象 -
Rx<T>的get方法中调用了Notifier.read, 此时将NotifierData对象中getUpate()的添加到contorller列表中 -
Rx<T>的set方法中调用了controller的refresh()触发了widget重建,
var name = 'Jonatas Borges'.obs;Obx(() => Text("${controller.name}")); -
obx简单应用
- 创建
GetxController obs观察值view中使用GetxController, 通过GetInstance.put添加controller,GetInstance.find查找controllerobx提供getUpate()obs的get方法, 添加getUpate()到controller.list<func>列表中obs的set方法触发controller的refresh()
class Controller extends GetxController{
var count = 0.obs;
increment() => count.value++;
}
class Home extends StatelessWidget {
@override
Widget build(context) {
// 使用Get.put()实例化你的类,使其对当下的所有子路由可用。
final Controller c = Get.put(Controller());
return Scaffold(
// 使用Obx(()=>每当改变计数时,就更新Text()。
appBar: AppBar(title: Obx(() => Text("Clicks: ${c.count.value}"))),
// 用一个简单的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));
}
}
Workers
ever实际上为GetListenable.listen添加监听
ever(count1, (_) => print("$_ has been changed"));
everAll批量添加监听once, 先GetListenable.listen添加监听, 收到onData监听后,移除观察者, 因此只执行一次interval, 先GetListenable.listen添加监听, 收到onData监听后,delayed来控制执行debounce, 先GetListenable.listen添加监听, 收到onData监听后,Timer来控制执行
路由
Get.to(NextScreen()); 相当于 GetDelegate.to(NextScreen())
Get.back(); 相当于 GetDelegate.back()
-
GetDelegate相当于对Navigator2.0做了一些封装, 另外通过维护一个数组List<RouteDecoder>来实现页面的切换 -
ParseRouteTree路由表, 包含应用所有路由信息 -
RouteDecoder路由的配置信息, path + arguments + params -
GetPage基于路由配置信息生成的页面
问题
问题1
data.disposers.isEmpty 它为空的可能性只有一个, Notifier.read, 没有被调用
Notifier.read 有两个作用:
- 添加
dispose - 向
list<func>添加函数
Notifier.read 在 StateMixin<T> 即通过 T 的get调用
mixin StateMixin<T> on ListNotifier {
@protected
T get value {
reportRead();
return _value as T;
}
}
因此 T.obs 之后, 必须要使用, 使用即会调用 T.get
问题2: getx路由中多个重复页面对应一个控制器的问题
解法一: 推荐
首先Bindings 使用create 即每次跳转都创建新的控制器
widget 要继承 GetWidget, 路由跳转时,preventDuplicates(去重)改为false
解法二: 适用部分情况
使用tag做标记, tag 一般为详情页id
此处和lazyput对应, 通过tag获取控制器