Flutter - GetX Helper 助你规范应用 tag

1,782 阅读4分钟

欢迎关注微信公众号:FSA全栈行动 👋

一、前言

项目从一开始就选择了 GetX 做为我们的状态管理方案,但是 GetX 对 Logic 的理想使用是一个类型仅存在一个实例的方式,而同一类型不同物件的详情页,则需要我们通过 tag 的方式去解决。

在这种情况下,在各个子 Widget 中想要去获取相应的 logic,就需要到处写相同的 tag 或者直接传 logic 实例,十分恶心。

我见过解决这个问题的一个方案,核心逻辑是以 Map + List 的方式去存储 tag,通过 Logic 类型从 Map 获取 List,再直接获取 List 的最后一个数据,删除也是直接操作最后一个,但是这样是不可靠的。

比如打开并替换当前页这个场景,新页面会添加 tag 到 List 中,被替换的页面会将这个 tag 移除,而在此时被替换页面的 logic 被移除,但是其对应的 tag 还在,且为 List 里的最后一个,然后新页面拿着这个旧的 tag 去获取 logic,就会出现 XXXController" not found. You need to call "Get.put(XXXController())" or "Get.lazyPut(()=>XXXController())" 的错误~

当然不同的小伙伴对 GetX 的使用有着不同的理解,也会导致各种各样的问题,如果你有遇到其它的使用场景问题,欢迎留言分享。

我自己也实现了一套方案,以页面为维度,安全且方便地获取其 logic 和 logicTag,并且规范使用方式。

github.com/LinXunFeng/…

二、集成

打开 pub.dev/packages/ge… ,复制最新版本。

将 getx_helper 添加到你的 pubspec.yaml 中,并执行 flutter pub get

dependencies:
  getx_helper: latest_version

在需要使用的地方中导入 getx_helper

import 'package:getx_helper/getx_helper.dart';

三、插件

1、安装

打开 vscode 的扩展,搜索 GetX Helper 并安装

安装完成后,在 Flutter 项目下,目录右击的菜单中会多如下选项

2、使用

这里假设你会将所有的页面都存放在 feature 目录,结构如下

.
├── main.dart
└── feature

右击 feature 目录,在菜单中选择 GetX: Simple Page,输入 home 回车,即可快速创建一个简单页面

.
├── main.dart
└── feature
    └── home
        ├── header
        │   └── home_header.dart
        ├── logic
        │   └── home_logic.dart
        ├── page
        │   └── home_page.dart
        └── state
            └── home_state.dart

如果你想拆分 Widget,可按如下步骤:

  1. 先去到 home 目录自行创建一个 widget 目录
  2. 然后右击该 widget 目录,选择 GetX: Mixin Widget
  3. 输入 Widget名,如 home_body_view,回车

这样即可快速生成一个模板 Widget,你可以在该模板 Widget 里直接拿到 logic 和 logicTag。

四、结构说明

此时的目录结构如下

.
├── main.dart
└── feature
    └── home
        ├── header
        │   └── home_header.dart
        ├── logic
        │   └── home_logic.dart
        ├── page
        │   └── home_page.dart
        ├── state
        │   └── home_state.dart
        └── widget
            └── home_body_view.dart

结构的分层说明如下

文件作用
header声明类型,定义枚举、视图刷新 ID,存放业务所需的常量数据等
logic处理业务逻辑
page页面结构,可直接拿到 logic 和 logicTag
state数据存储,可通过 logic.state 进行访问
widget拆分的子部件,可直接拿到 logic 和 logicTag

五、代码说明

header

在 header 文件中定义了两个 Mixin

typedef HomeLogicPutMixin<W extends StatefulWidget>
    = GetxLogicPutStateMixin<HomeLogic, W>;

typedef HomeLogicConsumerMixin<W extends StatefulWidget>
    = GetxLogicConsumerStateMixin<HomeLogic, W>;
  • PutMixin: page 使用,用于从页面根部提供 logicTag
  • ConsumerMixin: widget 使用,用于获取 logicTag 和 logic

page

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => HomePageState();
}

class HomePageState extends State<HomePage> with HomeLogicPutMixin<HomePage> {
  @override
  HomeLogic initLogic() => HomeLogic();
  
  // @override
  // String? customLogicTag() {
  //   return "https://github.com/LinXunFeng/getx_helper";
  // }

  @override
  Widget buildBody(BuildContext context) {
    // with 了 PutMixin 后,可直接获取到 logic 和 logicTag
    // lxf -- Instance of 'HomeLogic' -- 20651749399422401 -- Instance of 'HomeState'
    print('lxf -- $logic -- $logicTag -- ${logic.state}');

    return GetBuilder<HomeLogic>(
      tag: logicTag,
      builder: (_) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Home'),
            centerTitle: true,
          ),
          body: const HomeBodyView(),
        );
      },
    );
  }
}

步骤

  1. PutMixin 用于页面的 State 进行 with
  2. 重写 initLogic 方法返回 logic 实例
  3. 重写 buildBody 方法,该方法充当以前的 build 方法
  4. 如果你想自定义 logicTag 的值,可以重写 customLogicTag 方法

在 with 了 PutMixin 后,可直接获取 logic 和 logicTag。

initState

需要注意的是,如果你是在 initState 方法中去使用 logic 和 logicTag,则请先执行 super.initState(),如:

@override
void initState() {
  super.initState();

  debugPrint('logic -- $logic');
  debugPrint('state -- ${logic.state}');
  debugPrint('tag -- $logicTag');
}

keepAlive

如果你 with 了 PutMixin,那 with AutomaticKeepAliveClientMixin 就不再适用,需要调整为使用 KeepAliveWidget 去包裹。

class HomePageState extends State<HomePage> with HomeLogicPutMixin<HomePage> {
  ...
  @override
  Widget buildBody(BuildContext context) {
    Widget resultWidget = GetBuilder<HomeLogic>(
      tag: logicTag,
      ...
    );
    resultWidget = KeepAliveWidget(
      child: resultWidget,
    );
    return resultWidget;
  }
}
import 'package:flutter/material.dart';

class KeepAliveWidget extends StatefulWidget {
  const KeepAliveWidget({
    super.key,
    required this.child,
  });

  final Widget child;

  @override
  State<KeepAliveWidget> createState() => _KeepAliveWidgetState();
}

class _KeepAliveWidgetState extends State<KeepAliveWidget>
    with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;

  @override
  Widget build(BuildContext context) {
    super.build(context);
    return widget.child;
  }
}

widget

class HomeBodyView extends StatefulWidget {
  const HomeBodyView({super.key});

  @override
  State<HomeBodyView> createState() => _HomeBodyViewState();
}

class _HomeBodyViewState extends State<HomeBodyView>
    with HomeLogicConsumerMixin<HomeBodyView> {
  @override
  Widget build(BuildContext context) {
    // with 了 ConsumerMixin 后,可直接获取到 logic 和 logicTag
    // lxf -- Instance of 'HomeLogic' -- 20651749399422401 -- Instance of 'HomeState'
    print('lxf -- $logic -- $logicTag -- ${logic.state}');

    return const Center(
      child: Text('Home body'),
    );
  }
}

ConsumerMixin 用于 Widget 的 State 进行 with,在 with 了 ConsumerMixin 后,也可直接获取 logic 和 logicTag。

跟 PutMixin 一致,如果你是在 initState 方法中去使用,则请先执行 super.initState()。

六、最后

手敲这些文件和代码确实会很复杂,但有了相应的插件,这些都变得极为简单,不仅统一了使用规范,还提高了工作效率。

开源不易,如果你也觉得这个库好用 (github.com/LinXunFeng/…),请不吝给个 Star 👍 ,并多多支持!

好了,以上介绍的是针对新页面的使用姿势,对于已有页面要如何使用,请等待下一篇~

如果文章对您有所帮助, 请不吝点击关注一下我的微信公众号:FSA全栈行动, 这将是对我最大的激励. 公众号不仅有 Flutter 技术,还有 AI、Android,iOS,Python 等文章, 可能有你想要了解的技能知识点哦~