likes
comments
collection
share

Flutter 入门与实战(八十):使用GetX构建更优雅的页面结构

作者站长头像
站长
· 阅读数 19

前言

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,即 GetViewGetView是一个继承 StatelessWidget的抽象类,实现很简单,只是定义了一个获取 controllerget 属性。

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...elseswitch 语句。例如我们的个人主页,可以按下面的方式来写,是不是感觉更清晰和清爽了?

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'),
);

总结

本篇介绍了使用GetXStateMixin方式构建更优雅的页面结构,通过controller.obx 的参数配置不同状态对应不同的组件。可以根据 RxStatus 状态自动切换组件,而无需写丑陋的 if...elseswitch 语句。当然,使用这种方式的前提是需要在 GetX 的容器中构建 controller 对象,本篇源码已上传至:GetX 状态管理源码。实际上使用容器能够带来其他的好处,典型的应用就是依赖注入(Dependency Injection,简称DI),接下来我们会使用两篇来介绍依赖注入的概念和具体应用。