写点什么

GetX 代码生成 IDEA 插件,超详细功能讲解(透过现象看本质)

用户头像
小呆呆666
关注
发布于: 2021 年 09 月 07 日
GetX代码生成IDEA插件,超详细功能讲解(透过现象看本质)

前言


本文更新非常频繁,最新内容请查看:最新内容---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

请注意:isPageView 和 autoDispose 按钮不能同时选中,他们俩都能解决 PageView 中存的问题,选择其中一按钮,另一按钮会自动取消勾选


这算是一个非常有用的功能了


如果大家在 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); } } }}
复制代码


  • 使用非常的简单


/// 回收单个GetXControllerclass TestPage extends StatelessWidget {  final logic = Get.put(TestLogic());
@override Widget build(BuildContext context) { return GetBindWidget( bind: logic, child: Container(), ); }}
/// 回收多个GetXControllerclass 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

pub:lint库


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



但是,这个功能,真是少部分强迫症患者的福音


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


lint 是一个严格规则的代码库,对于代码相应不规范的地方,会通过 IDEA 给与提示;对于我们很多认为合理的代码,有时候可能也会给出相应的警告


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

  • 这俩个注入代码,都会自动推导出对应的类型;但是在 lint 规则下,会有黄色下划线警告



  • 需要做这样的调整,才能去掉警告



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


对于用 lint 这种强规则的人,我表示:



pub:flutter_lints


最近 Flutter 在新建项目里面,默认加上了 flutter_lints 这个库,这个库的规则宽松很多,规则基本也是规范 flutter 的写法


  • 生成了模板代码里面,会有一个警告



  • 需要做如下调整,才能去掉警告



当你开启 lintNorm,也会帮你补上生成页面的构造函数

template(切换模板命名)

场景

该功能提供了切换模板命名的操作


提供三套模板命名,只提供三套,不会再多增了


内部对持久化模块进行了重构


  • 不重构不行,增加了大量的持久化变量,还全部使用静态变量着实不优雅

  • 增加了数据类,来记录大量重复的持久化数据


为什么要提供切换模板命名的功能?


当业务逐渐的复杂,很多时候,复杂的通用组件,也可以使用 getx 去封装


  • 但是使用插件生成对应模块,view 模块的 Widget 可能还是为 XxxPage

  • 上面这种情况就不太友好了,你可能需要 XxxComponent 或者 XxxWidget

  • 虽然,可以在设置里重命名后缀名,但是这样可能又对生成 Page 模块产生影响

  • 所以,这里提供三套模板命名切换,可以快速切换到你需要的自定命名方式

功能演示

插件窗口增加三套模板切换


  • 选择 Template,提供三套切换模板命名:Page、Component、Custom

  • 默认 Page



三套模板命名都支持自定义修改


  • 上面的切换,对应设置页面的三套自定义通用后缀

  • 设置页面布局也重写了,看起来更舒服一些,对整体空间利用率也更高了



示例


  • mode 选择:Easy;Template 选择:Component



看下代码


  • view


class TestComponent extends StatelessWidget {  final logic = Get.put(TestLogic());
@override Widget build(BuildContext context) { return Container(); }}
复制代码


  • logic


class TestLogic extends GetxController {
}
复制代码

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.2.x


  • 增加模板切换功能,大幅度优化内部持久化方式

  • 重构设置页面布局

  • 支持 flutter_lints 规则


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;提之前:请先思考下,合不合理

最后

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


感谢大家提的各种蛋痛的需求



能让这个插件一点点的完善,以至于现在,,能真正的帮助靓仔们节省一点开发时间



系列文章 + 相关地址


发布于: 2021 年 09 月 07 日阅读数: 114
用户头像

小呆呆666

关注

2021,葬爱不在低调 2020.08.17 加入

还未添加个人简介

评论

发布
暂无评论
GetX代码生成IDEA插件,超详细功能讲解(透过现象看本质)