前言
App 的大部分页面都会涉及到数据加载、错误、无数据和正常几个状态,在一开始的时候我们可能数据获取的状态枚举用 if...else
或者 switch
来显示不同的 Widget
,这种方式会显得代码很丑陋,譬如下面这样的代码:
if (PersonalController.to.loadingStatus == LoadingStatus.loading) {
return Center(
child: Text('加载中...'),
);
}
if (PersonalController.to.loadingStatus == LoadingStatus.failed) {
return Center(
child: Text('请求失败'),
);
}
// 正常状态
PersonalEntity personalProfile = PersonalController.to.personalProfile;
return Stack(
...
);
这种情况实在是不够优雅,在 GetX 中提供了一种 StateMixin
的方式来解决这个问题。
StateMixin
StateMixin
是 GetX 定义的一个 mixin
,可以在状态数据中混入页面数据加载状态,包括了如下状态:
-
RxStatus.loading()
:加载中; -
RxStatus.success()
:加载成功; -
RxStatus.error([String? message])
:加载失败,可以携带一个错误信息message
; -
RxStatus.empty()
:无数据。
StateMixin 的用法如下:
class XXXController extends GetxController
with StateMixin<T> {
}
其中 T 为实际的状态类,比如我们之前一篇 PersonalEntity,可以定义为:
class PersonalMixinController extends GetxController
with StateMixin<PersonalEntity> {
}
然后StateMixin
提供了一个 change
方法用于传递状态数据和状态给页面。
void change(T? newState, {RxStatus? status})
其中 newState
是新的状态数据,status
就是上面我们说的4种状态。这个方法会通知 Widget
刷新。
GetView
GetX 提供了一个快捷的 Widget
用来访问容器中的 controller
,即 GetView
。GetView
是一个继承 StatelessWidget
的抽象类,实现很简单,只是定义了一个获取 controller
的 get
属性。
abstract class GetView<T> extends StatelessWidget {
const GetView({Key? key}) : super(key: key);
final String? tag = null;
T get controller => GetInstance().find<T>(tag: tag)!;
@override
Widget build(BuildContext context);
}
通过继承 GetView
,就可以直接使用controller.obx
构建界面,而 controller.obx
最大的特点是针对 RxStatus
的4个状态分别定义了四个属性:
Widget obx(
NotifierBuilder<T?> widget, {
Widget Function(String? error)? onError,
Widget? onLoading,
Widget? onEmpty,
})
- NotifierBuilder<T?> widget:实际就是一个携带状态变量,返回正常状态界面的函数,NotifierBuilder<T?>的定义如下。通过这个方法可以使用状态变量构建正常界面。
typedef NotifierBuilder<T> = Widget Function(T state);
-
onError
:错误时对应的Widget
构建函数,可以使用错误信息error
。 -
onLoading
:加载时对应的Widget
; -
onEmpty
:数据为空时的Widget
。
通过这种方式可以自动根据 change
方法指定的 RxStatus
来构建不同状态的 UI 界面,从而避免了丑陋的 if...else
或 switch
语句。例如我们的个人主页,可以按下面的方式来写,是不是感觉更清晰和清爽了?
class PersonalHomePageMixin extends GetView<PersonalMixinController> {
PersonalHomePageMixin({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return controller.obx(
(personalEntity) => _PersonalHomePage(personalProfile: personalEntity!),
onLoading: Center(
child: CircularProgressIndicator(),
),
onError: (error) => Center(
child: Text(error!),
),
onEmpty: Center(
child: Text('暂无数据'),
),
);
}
}
对应的PersonalMixinController的代码如下:
class PersonalMixinController extends GetxController
with StateMixin<PersonalEntity> {
final String userId;
PersonalMixinController({required this.userId});
@override
void onReady() {
getPersonalProfile(userId);
super.onReady();
}
void getPersonalProfile(String userId) async {
change(null, status: RxStatus.loading());
var personalProfile = await JuejinService().getPersonalProfile(userId);
if (personalProfile != null) {
change(personalProfile, status: RxStatus.success());
} else {
change(null, status: RxStatus.error('获取个人信息失败'));
}
}
}
Controller 的构建
从 GetView 的源码可以看到,Controller 是从容器中获取的,这就需要使用 GetX 的容器,在使用 Controller 前注册到 GetX 容器中。
Get.lazyPut<PersonalMixinController>(
() => PersonalMixinController(userId: '70787819648695'),
);
总结
本篇介绍了使用GetX
的 StateMixin
方式构建更优雅的页面结构,通过controller.obx
的参数配置不同状态对应不同的组件。可以根据 RxStatus
状态自动切换组件,而无需写丑陋的 if...else
或 switch
语句。当然,使用这种方式的前提是需要在 GetX
的容器中构建 controller
对象,本篇源码已上传至:GetX 状态管理源码。实际上使用容器能够带来其他的好处,典型的应用就是依赖注入(Dependency Injection,简称DI),接下来我们会使用两篇来介绍依赖注入的概念和具体应用。