乐趣区

关于android:GetX代码生成IDEA插件超详细功能讲解透过现象看本质

前言

本文章不是写 getx 框架的应用,而且其代码生成 IDEA 插件的性能解说

我之前写过俩篇很长很长的 getx 文章

一篇入门应用:Flutter GetX 应用 — 简洁的魅力!

一篇原理深度分析:Flutter GetX 深度分析 | 咱们终将走出本人的路(万字图文)

鱼和渔都曾经交给大家了,就没必要去赘述了

同时,我也写了一个 getx 代码生成插件:getx_template,这个工具相当于钓鱼座椅(让你更难受的钓鱼或吃鱼?)吧!初期性能非常简略,就是生成单页面相应的模块代码,连个记忆选项性能都没有,基本上就是个塑料座椅的水平

  • 然而随着大量 叼毛 靓仔 给我提的各种需要,这个插件变的曾经有点简单了
  • 尤其是波及 Select Function 模块,有些人可能都搞不懂选中的性能按钮是啥意思,就一通全副勾中。。。
  • 所以,本凤雏想具体的,和各位卧龙谈谈这个工具方方面面的性能,心愿能帮忙各位节俭点开发工夫

兄弟们,我切实不想写水文;然而这个工具一个性能按钮,扭转的代码可能很少,其背地所蕴含的货色,可能须要大量的笔墨去形容,这边就对立的和各位彦祖于宴亦菲们,说道说道。

本文长期更新,如果想晓得插件每次具体更新内容,能够点进来看。

代码生成

  • Plugins 里搜寻 getx 即可

比照

  • 晚期代码生成弹框,可选性能比拟少,过后还不反对长久化贮存

    • 淦,图标也丑

  • 这是屡次欠缺后的性能抉择弹窗

鄙人是个十足的颜值党,这次最新版本的页面,我做了很多考量

  • 首页随着各位靓仔提的各种需要,Select Function,从最后的俩个性能,减少到当初的七个性能

    • 随着性能按钮的增多,在 dialog 上平铺下来,整个 dialog 的高度会变得相当的长
    • 最重要的是:会让使用者,不明确 Function 外面的重点性能按钮是什么!
  • 基于上述的思考,我搜索枯肠的想解决这个问题

    • 计划一:我原本是想做一个折叠收纳区域,主要性能按钮放在折叠区域中

      • 用 swing 一通写后,发现成果是真的丑,收纳的时候,高度计算也有问题:放弃
    • 计划二:这个是我在翻 swing 控件的时候,发现了 JBTabbedPane 这个 tab 控件

      • 成果简洁优雅,完爆折叠思路:采纳
  • 这次我全面的改善了 dialog 布局问题

    • 以前的整个 dialog 的长宽是写死的,在高尺寸的分辨率屏幕上会存在问题
    • 这次,发现了 pack 办法的妙用(swing 菜狗的辛酸泪),全面重构的界面布局逻辑
  • 这一次,在 48 寸的屏幕上,必定不会呈现上面这种状况了

尽管我没试,然而我对本人的代码有信念

模式抉择

这里提供俩种大的模式抉择:default,easy

来看下区别

default 模式

  • view
class TestPage extends StatelessWidget {final logic = Get.put(TestLogic());
  final state = Get.find<TestLogic>().state;

  @override
  Widget build(BuildContext context) {return Container();
  }
}
  • logic
class TestLogic extends GetxController {final TestState state = TestState();
}
  • state
class TestState {TestState() {///Initialize variables}
}

Easy 模式

  • view
class TestPage extends StatelessWidget {final logic = Get.put(TestLogic());

  @override
  Widget build(BuildContext context) {return Container();
  }
}
  • logic
class TestLogic extends GetxController {}

总结

下面的 default 模式和 easy 模式,从代码上看,还是能看出很显著的区别

  • Default 模式比 Easy 模式多了一个 State 层
  • State 是专门用来寄存页面变量和初始化相干变量数据的

我曾写过一个比较复杂模块

  • 页面的变量达到几百个(波及到简单的表单提交),与用户的事件交互也有几十个
  • 整个模块很多逻辑依附相干变量去标定,会初始化很多不同数据,State 层的代码简直快一千行
  • 所以当业务逐步的简单,State 层并不薄,他撑持着整个模块的逻辑标定和扭转

除非是肉眼可见的业务极简模块,举荐应用 Easy 模块;其余的状况举荐应用 Default 模式

次要性能(main)

useFolder,usePrefix

useFolder 和 usePrefix 性能比较简单,这里就放在一起讲了

useFolder

本项性能是默认选中的,会在创立的多个文件外,创立一个文件夹,方便管理

usePrefix

一些小伙伴喜爱在各层:view,state,logic,前加上 module 名的前缀(小写 + 下划线)

这边也为大家提供了一个这样的可选性能

isPageView

这算是一个十分有用的性能了

如果大家在 PageView 中应用 getx,可能会发现,所有的子页面中的 GetXController,一下全被注入了!并不是切换到对应页面,注入对应的 GetXController!

PageView(children: [FunctionPage(),
    ExamplePage(),
    SettingPage(),])

剖析

咱们能够来剖析下,为什么会产生这种状况,来看下:FunctionPage

class FunctionPage extends StatelessWidget {final logic = Get.put(TestLogic());
  final state = Get.find<TestLogic>().state;

  @override
  Widget build(BuildContext context) {return Container();
  }
}

咱们注入的步骤,是放在类的成员变量作用域

  • 这个作用域是在实例化构造函数之前起效的
  • 所以咱们在增加被实例的 Page 的时候,成员变量的作用域间接被触发,GetXController 就被注入

PageView 触发机制

  • PageView 触发被增加 Widget,是触发对应 Widget 的 build 办法
  • 切换到哪个 Widget,就触发对应 Widget 的 build 办法

有了下面这层了解,就很容易解决 PageView 的问题了

  • 只须要将注入过程放在 build 办法中
  • 因为咱们应用的是 StatelessWidget,并不需要思考其刷新问题,只有它的父节点刷新,它才会被刷新
  • GetX 存储对象应用的 putIfAbsent 办法,只会存储第一次注入对象,后续雷同类的对象间接疏忽,这能防止很多问题

解决

所以此性能只须要扭转 View 文件里,GetXController 的注入地位,其它文件不须要变动

addBinding

binding 是为了对立治理 GetXController,来看下 binding 和非 binding 的区别

非 Binding

  • view
class TestOnePage extends StatelessWidget {final logic = Get.put(TestOneLogic());

  @override
  Widget build(BuildContext context) {return Container();
  }
}
  • logic
class TestOneLogic extends GetxController {}

Binding:须要配套 GetX 路由

  • binding
class TestTwoBinding extends Bindings {
  @override
  void dependencies() {Get.lazyPut(() => TestTwoLogic());
  }
}
  • view
class TestTwoPage extends StatelessWidget {final logic = Get.find<TestTwoLogic>();

  @override
  Widget build(BuildContext context) {return Container();
  }
}
  • logic
class TestTwoLogic extends GetxController {}
  • 须要在路由模块绑定下这个 binding
void main() {runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: RouteConfig.testOne,
      getPages: RouteConfig.getPages,
    );
  }
}

class RouteConfig {
  static const String testTwo = "/testTwo";

  static final List<GetPage> getPages = [
    GetPage(
      name: testTwo,
      page: () => TestTwoPage(),
      binding: TestTwoBinding(),),
  ];
}

总结

binding 文件外面,应用的是懒注入:在应用了 find 办法的时候,才会真正的注入

所以在 view 外面,就须要将 put 改成 find 就行了,总结下

  • 减少 binding 文件,应用懒注入
  • view 文件,put 改成 find
  • 须要在 getx 路由模块,对应的页面上绑定 binding 实例

主要性能(minor)

addLifecycle

这是个非常简单的性能,就放在主要性能 tab 下

一些小伙伴,logic 模块须要常常写 onReady 和 onClose 回调,懒得每次手写;所以在插件里增加了主动补上这俩个回调的性能

  • 仅仅 Logic 文件有区别

autoDispose

该性能正如名字一样:主动开释 GetXController

实际上,这是个十分重要的性能,然而实现的太不优雅了,就把它移到了主要性能 tab 外面了

GetX 外部对回收 GetXController,做了很多解决,开释的操作是在 GetX 路由解决的;然而,业务多变简单,导致某些 GetXController 很难被框架主动开释,例如:

  • PageView 的子页面
  • 应用 GetX 封装的简单组件
  • 不应用 GetX 路由

下面的这些状况都无奈主动回收 GetXController;为此,我在插件里,给出了一个解决方案,区别只在 view 文件

通用解决方案

  • view
class TestTwoPage extends StatefulWidget {
  @override
  _TestTwoPageState createState() => _TestTwoPageState();
}

class _TestTwoPageState extends State<TestTwoPage> {final logic = Get.put(TestTwoLogic());

  @override
  Widget build(BuildContext context) {return Container();
  }

  @override
  void dispose() {Get.delete<TestTwoLogic>();
    super.dispose();}
}
  • logic
class TestTwoLogic extends GetxController {}

下面这种计划,是根本都能解决回收 GetXController 问题(除非你手动开启保活 GetXController 的参数)

然而!这外面须要应用 StatefulWidget!多了很多代码!这太不优雅了!

优化解决方案

下面的是个通用解决办法,你不须要额定的引入任何其它的货色;然而这种计划用到了 StatefulWidget,代码多了一大坨,让我有点膈应

鄙人有着相当的强迫症,想了很久

  • 原本是想 GetBuilder 写个回收逻辑,而后提个 PR 给作者

    • 发现 getx 框架曾经做了这样的解决,然而,须要配套一个参数开启应用
    • 在 GetBuilder 外面写了回收逻辑:对 Obx 刷新模块无奈起效,Obx 刷新控件外部无奈定位到 GetXController,所以无奈做回收操作
  • 那只能从内部动手,我就写了一个通用控件,来对相应的 GetXController 进行回收

    • 这个通用控件,我也给 getx 提了 PR,始终在审核
    • 就算这个控件的 PR 通过了,集成到 getx 中,getx 低版本也无奈应用,没辙
    • 这边我给出这个通用回收控件代码,各位能够自行复制到我的项目中应用

GetBindWidget

  • 该控件能够回收单个 GetXController(bind 参数),能够加上对应 tag(tag 参数);也能够回收多个 GetXController(binds),能够加上多个 tag(tags 参数,请和 binds 一 一 对应;无 tag 的 GetXController 的,tag 能够写成空字符:””)
import 'package:flutter/material.dart';
import 'package:get/get.dart';

/// GetBindWidget can bind GetxController, and when the page is disposed,
/// it can automatically destroy the bound related GetXController
///
///
/// Sample:
///
/// class SampleController extends GetxController {
///   final String title = 'My Awesome View';
/// }
///
/// class SamplePage extends StatelessWidget {///   final controller = SampleController();
///
///   @override
///   Widget build(BuildContext context) {
///     return GetBindWidget(
///       bind: controller,
///       child: Container(),
///     );
///   }
/// }
class GetBindWidget extends StatefulWidget {
  const GetBindWidget({
    Key? key,
    this.bind,
    this.tag,
    this.binds,
    this.tags,
    required this.child,
  })  : assert(
          binds == null || tags == null || binds.length == tags.length,
          'The binds and tags arrays length should be equal\n'
          'and the elements in the two arrays correspond one-to-one',
        ),
        super(key: key);

  final GetxController? bind;
  final String? tag;

  final List<GetxController>? binds;
  final List<String>? tags;

  final Widget child;

  @override
  _GetBindWidgetState createState() => _GetBindWidgetState();
}

class _GetBindWidgetState extends State<GetBindWidget> {
  @override
  Widget build(BuildContext context) {return widget.child;}

  @override
  void dispose() {_closeGetXController();
    _closeGetXControllers();

    super.dispose();}

  ///Close GetxController bound to the current page
  void _closeGetXController() {if (widget.bind == null) {return;}

    var key = widget.bind.runtimeType.toString() + (widget.tag ?? '');
    GetInstance().delete(key: key);
  }

  ///Batch close GetxController bound to the current page
  void _closeGetXControllers() {if (widget.binds == null) {return;}

    for (var i = 0; i < widget.binds!.length; i++) {var type = widget.binds![i].runtimeType.toString();

      if (widget.tags == null) {GetInstance().delete(key: type);
      } else {var key = type + (widget.tags?[i] ?? '');
        GetInstance().delete(key: key);
      }
    }
  }
}
  • 应用十分的简略
/// 回收单个 GetXController
class TestPage extends StatelessWidget {final logic = Get.put(TestLogic());

  @override
  Widget build(BuildContext context) {
    return GetBindWidget(
      bind: logic,
      child: Container(),);
  }
}

/// 回收多个 GetXController
class TestPage extends StatelessWidget {final logicOne = Get.put(TestLogic(), tag: 'one');
  final logicTwo = Get.put(TestLogic());
  final logicThree = Get.put(TestLogic(), tag: 'three');

  @override
  Widget build(BuildContext context) {
    return GetBindWidget(binds: [logicOne, logicTwo, logicThree],
      tags: ['one', '','three'],
      child: Container(),);
  }
}

/// 回收日志
[GETX] Instance "TestLogic" has been created with tag "one"
[GETX] Instance "TestLogic" with tag "one" has been initialized
[GETX] Instance "TestLogic" has been created
[GETX] Instance "TestLogic" has been initialized
[GETX] Instance "TestLogic" has been created with tag "three"
[GETX] Instance "TestLogic" with tag "three" has been initialized
[GETX] "TestLogicone" onDelete() called
[GETX] "TestLogicone" deleted from memory
[GETX] "TestLogic" onDelete() called
[GETX] "TestLogic" deleted from memory
[GETX] "TestLogicthree" onDelete() called
[GETX] "TestLogicthree" deleted from memory

总结

对于下面的优化计划

  • 就算你不应用 GetX 路由,你也能够很轻松的回收对应的 GetXController 了
  • 这种回收形式在 GetBuilder 和 Obx 俩种刷新机制中,都是通用的
  • 回收的机会:是以后页面被回收的时候

惟一麻烦的:须要你手动把 GetBindWidget 这个控件,引入到本人的我的项目中

LintNorm

这个性能,乍一看,大家预计都懵逼了;这要不是我写的,我看了也懵逼啊

然而,这个性能,真是少部分强迫症患者的福音

因为 getx 作者,在 demo 我的项目外面,引入的 lint 库,一些小伙伴可能也用了这个库

lint(pub):https://pub.dev/packages/lint

lint 是一个严格规定的代码库,对于代码相应不标准的中央,会通过 IDEA 给与提醒;对于咱们很多认为正当的代码,有时候可能也会给出相应的正告

  • 在生成的模板代码,有几行就会在 lint 规定下被正告

    • 这俩个注入代码,都会主动推导出对应的类型;然而在 lint 规定下,会有黄色下划线正告

  • 须要做这样的调整,能力去掉正告

选中 lintNorm 按钮,就会以上面这种模式生成模板代码;所以说这个性能是强迫症患者福音。。。

对于用 lint 这种强规定的人,我示意:

通用后缀批改

  • 反对通用后缀名批改

Wrap Widget

这是一个十分好用的性能

目前反对四种 Wrap Widget 类型:GetBuilder,GetBuilder(autoDispose),Obx,GetX

应用注意事项:鼠标点击在 Widget 上即可,而后按 alt+enter;请勿双击选中 Widget 名字

  • GetBuilder

  • GetBuilder(Auto Dispose)

    • assignId 设置为 true:GetBuilder 就会在页面被回收的时候,主动回收其指定泛型的 GetXController

  • Obx

    • 说下这里为什么不必箭头符号,如果须要包裹的 Widget 十分长的话,应用箭头符号后,格式化后的代码并不参差
    • 思考到这种状况,所以应用了 return 模式

  • GetX

    • 这个组件我尽管不太喜爱用,然而指不定有喜爱用的小伙伴,就加上了

  • 可选择性敞开

快捷代码生成

插件也为大家提供了,输出关键字生成快键代码片段的性能

请留神:关键字前缀为getx

路由模块

  • getxroutepagemap

  • getxroutename

  • getxroutepage

  • getxto,getxtoname

  • getxoff,getxoffall,getxoffnamed,getxoffallnameed

依赖注入

  • put

  • find

  • lazyPut

业务层

  • GetxController

  • getxfinal,getxfinal_

  • getxget,getxget_

  • getset,getset_

其它

  • getsnakebar,getdialog,getbottomsheet

  • getxbuilder,getxobx

  • binding

还有其它的一些快捷代码,自行感触喽~~

版本更新阐明

3.1.x

  • 显著的晋升整体页面布局

    • 高尺寸屏幕不会再呈现坑比问题了
  • 反对 lint 规定(lintNorm)
  • 改善快捷代码提醒性能,“get”前缀改成为“getx”

    • getx 为前缀,会让提醒代码被很多零碎代码吞没,改为 getx 之后就能够高深莫测了

3.0.x

  • 我的项目代码从 Java 迁徙为 kotlin
  • ModuleName 输出:首字母小写,外部会主动标为大写
  • 减少大量快捷代码片段生成
  • 插件我的项目逻辑重构,界面层和逻辑层拆散
  • Wrap Widget 减少:GetBuilder(Auto Dispose)

    • 可主动回收对应的 GetXController
  • 减少 PageView 解决方案
  • 修复一些 bug

2.1.x

  • 重大更新!
  • 减少 Wrap Widget:GetBuilder,Obx,GetX
  • 减少快捷代码片段生成
  • 大幅度优化插件布局
  • 减少欠缺生命周期回调性能(addLifecycle)
  • 增加 binding 性能(addBinding)

1.5.x

  • 减少记忆性能(记忆抉择的按钮)
  • 增加 GetXController 主动回收性能(autoDispose)
  • 反对批改通用后缀:view,logic,state
  • 调整插件阐明,修复一些 bug

1.3.x

  • 适配多版本的 IDEA(之前只适配了一个 IDEA 版本,坑)
  • 增加插件 logo
  • 减少一篇 getx 英文文章(机翻本人的博客文章)
  • 改善插件形容

1.2

  • 调整形容内容

1.1

  • 修复减少前缀时,产生的导包异样问题

1.0

  • 你能够应用本插件生成大量的 getx 框架代码
  • 这能大大晋升你的效率
  • 如果有任何问题,欢送给我提 issue;提之前:请先思考下,合不合理

最初

在不断完善这个插件的时候,也是我一直思考的一个过程,

感激大家提的各种蛋痛的需要

能让这个插件一点点的欠缺,以至于当初,,能真正的帮忙靓仔们节俭一点开发工夫

系列文章 + 相干地址

  • 插件的 Github 地址:getx_template
  • Flutter GetX 应用 — 简洁的魅力!
  • Flutter GetX 深度分析 | 咱们终将走出本人的路(万字图文)
退出移动版