Getx是一个第三方库,它为我们封装好了一些方法与功能,可以简化我们的代码编写功能。
状态管理
在使用Getx时,离不开使用他的状态管理,通常会从三中方式中选中使用
- put/create
- 通常在StatelessWidget中使用
- binds
- 与路由结合一起使用,页面继承GetView
- Obx
-
这个通常用于单个变量(也包括对象)值的改变,自动更新视图
-
put/create
先来说说使用
1, create 使用方式
Get.create(() => GetStateLogic());
将一个继承了GetxController的对象传给Get.create方法,内部转换后最终执行 _insert 方法,_insert 后面说
/// 此为Get.create方法
void create<S>(InstanceBuilderCallback<S> builder,
{String? tag, bool permanent = true}) =>
GetInstance().create<S>(builder, tag: tag, permanent: permanent);
/// 此为GetInstance().create方法
void create<S>(
InstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = true,
}) {
_insert(
isSingleton: false,
name: tag,
builder: builder,
permanent: permanent,
);
}
2, put 使用方式
- Get.put
class GetStatePage extends StatelessWidget {
//初始化controller
final logic = Get.put(GetStateLogic());
///获取state
final state = Get.find<GetStateLogic>().state;
}
将一个继承GetxController的类传入Get.put方法,Get.put会返回当前传入的对象因此可以用于初始化对象,经过内部转换后,最终执行 _insert 方法,_insert 后面说
/// 此为Get.put方法
S put<S>(S dependency,
{String? tag,
bool permanent = false,
InstanceBuilderCallback<S>? builder}) =>
GetInstance().put<S>(dependency, tag: tag, permanent: permanent);
/// 此为GetInstance().put方法
S put<S>(
S dependency, {
String? tag,
bool permanent = false,
@deprecated InstanceBuilderCallback<S>? builder,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder ?? (() => dependency));
return find<S>(tag: tag);
}
- Get.lazyPut
Get.putAsync<GetStateLogic>(
() => Future.value(GetStateLogic())
);
Get.put的懒加载模式,初始化时,并不是直接传入一个实体对象,而是传入一个有回调的方法,最终会走到 _insert方法,Get.putAsync不会返回任何值,只能用于插入到Get使用,而不能显示Get.put那样用于初始化对象使用
/// 此为Get.lazyPut方法
void lazyPut<S>(InstanceBuilderCallback<S> builder,
{String? tag, bool fenix = false}) {
GetInstance().lazyPut<S>(builder, tag: tag, fenix: fenix);
}
/// 此为GetInstance().lazyPut方法
void lazyPut<S>(
InstanceBuilderCallback<S> builder, {
String? tag,
bool? fenix,
bool permanent = false,
}) {
_insert(
isSingleton: true,
name: tag,
permanent: permanent,
builder: builder,
fenix: fenix ?? Get.smartManagement == SmartManagement.keepFactory,
);
}
- Get.putAsync
Get.putAsync<GetStateLogic>(
() => Future.value(GetStateLogic()),
);
某些时候,需要经过耗时/异步处理,拿到数据后再初始化controller,Get.putAsync需要传入一个Future,同时该方法也会返回一个Future,经过内部转换,最终调用Get.put进行插入,核心点就在于,调用Get.put时,传入的是一个 await 修饰的方法
/// 此为 Get.putAsyn方法
Future<S> putAsync<S>(AsyncInstanceBuilderCallback<S> builder,
{String? tag, bool permanent = false}) async =>
GetInstance().putAsync<S>(builder, tag: tag, permanent: permanent);
/// 此为 GetInstance().putAsync 方法
Future<S> putAsync<S>(
AsyncInstanceBuilderCallback<S> builder, {
String? tag,
bool permanent = false,
}) async {
return put<S>(await builder(), tag: tag, permanent: permanent);
}
上面说的都是如何使用,并最终执行到 _insert方法
/// 全局存储
static final Map<String, _InstanceBuilderFactory> _singl = {};
/// 获取key
String _getKey(Type type, String? name) {
return name == null ? type.toString() : type.toString() + name;
}
/// 插入到_singl
void _insert<S>({
bool? isSingleton,
String? name,
bool permanent = false,
required InstanceBuilderCallback<S> builder,
bool fenix = false,
}) {
final key = _getKey(S, name);
if (_singl.containsKey(key)) {
final dep = _singl[key];
if (dep != null && dep.isDirty) {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
lateRemove: dep as _InstanceBuilderFactory<S>,
);
}
} else {
_singl[key] = _InstanceBuilderFactory<S>(
isSingleton,
builder,
permanent,
false,
fenix,
name,
);
}
}
_insert 就是把当前Controller,以当前类型与Tag组合成key,存储到一个全局的Map对象中去。
再结合GetX的路由,自动调用Get.delete方法移除Controller
- 所有页面都需要注册到GetMaterialApp的getPages列表里去
- getPages是一个List< GetPage >
- GetPage继承Page,并且重写了createRoute方法
@override
Route<T> createRoute(BuildContext context) {
// return GetPageRoute<T>(settings: this, page: page);
final _page = PageRedirect(
route: this,
settings: this,
unknownRoute: unknownRoute,
).getPageToRoute<T>(this, unknownRoute);
return _page;
}
- 通过createRoute会把Route转化成GetPageRoute
- GetPageRoute混入了PageRouteReportMixin,实现了dispose方法
mixin PageRouteReportMixin<T> on Route<T> {
@override
void install() {
super.install();
RouterReportManager.reportCurrentRoute(this);
}
@override
void dispose() {
super.dispose();
RouterReportManager.reportRouteDispose(this);
}
}
- 每当视图被移除时会执行dispose
RouterReportManager.reportRouteDispose 调用 _removeDependencyByRoute 调用 GetInstance().delete
binds/binds
单个/多个绑定
/// 单个绑定
GetPage(
name: Paths.getBind,
page: () => BindPage(),
binding: BindBinding(),
)
/// 多个绑定
GetPage(
name: Paths.getBind,
page: () => BindPage(),
bindings: [
BindBinding(),
OtherBinding(),
],
)
在GetPage如果需要使用绑定,需要传入一个或者多个继承Bindings的类对象,继承Bindings需要重写 dependencies方法
class BindBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut(() => BindLogic());
}
}
通常会在dependencies中把对应页面的Controller插入到Get的Controller管理器中去,在需要使用的地方,通过Get.find()得到当前页的Controller使用
上面是如何使用,那么bind/binds是在什么时候被调用的呢
- 通过上面的_insert方法分析我们了解到,所有页面都会转成GetPageRoute
- GetPageRoute会继承PageRoute
- 当打开页面时,会执行GetPageRoute重写PageRoute中的buidPage方法
- buidPage->buildContent->_getChild
Widget _getChild() {
if (_child != null) return _child!;
final middlewareRunner = MiddlewareRunner(middlewares);
final localbindings = [
if (bindings != null) ...bindings!,
if (binding != null) ...[binding!]
];
final bindingsToBind = middlewareRunner.runOnBindingsStart(localbindings);
if (bindingsToBind != null) {
for (final binding in bindingsToBind) {
binding.dependencies();
}
}
final pageToBuild = middlewareRunner.runOnPageBuildStart(page)!;
_child = middlewareRunner.runOnPageBuilt(pageToBuild());
return _child!;
}
- 不管是单个绑定还是多个绑定,最终会合成为一个列表,并遍历调用binding.dependencies()
- binding.dependencies()就是BindBinding需要重写的方法
Obx
这个要分两个部分来说
- 变量定义(拿RxInt来说):变量的值改变时通知视图更新
- RxInt 继承Rx< T > 继承 _RxImpl 继承 RxNotifier 混入 RxObjectMixin
- RxNotifier = RxInterface with NotifyManager,RxObjectMixin混入了NotifyManage
- obx包裹视图:监听RxInt的数值变化
- obx 继承 ObxWidget = StatefulWidget
- 实例化RxNotifier对象,并把setstate方法传入进去,用于RxInt数值变化时回调
上面说了RxInt与obx是什么,干什么用的,那么他们是如何关联起来的呢 核心就在与RxInterface类中,现在我们按照执行顺序来分析
- 绑定步骤
- 第一步:在页面中,计算绘制视图时,执行obx
- 第二步:在obx对应的StatefulWidget中实例化_observer(_observer是一个Stream),传入setState()与_observer进行绑定,每当_observer有事件流来时,就会执行setState()方法
- 第三步:执行obx对应的StatefulWidget对应的Build方法,build方法执行RxInterface.notifyChangeChild,并传入_observer与widget.build
- 第四步:通过RxInterface类中的静态变量(中转使用),存储当前_observer,并执行widget.build
- 第五步:绘制widget.build时,会调用获取RxInt.value
- 第六步:调用RxInt.value实际执行的是RxInt最终继承的RxObjectMixin类中的T get value 方法(在被调用RxObjectMixin的value方法前,RxInt初始化时,获自动创建subject(Stream),用于在RxInt发生改变时,发送流事件)
- 第七步:把当前RxInt的subject传入进去,用于把RxInt值变化的Stream与页面刷新事件进行绑定
- 第八步:绑定完成后,恢复RxInterface的proxy
- 更新步骤
- 每当RxInt值发送改变时,最终都会执行RxObjectMixin类中的 set value方法
- 在set value 中,给subject 新增事件流
- 通过之前的绑定关系,刷新使用到这个RxInt的obx视图