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;提之前:请先思考下,合不合理
最后
在不断完善这个插件的时候,也是我不断思考的一个过程,
感谢大家提的各种蛋痛的需求
能让这个插件一点点的完善,以至于现在,,能真正的帮助靓仔们节省一点开发时间
系列文章 + 相关地址
GetX代码生成IDEA插件,超详细功能讲解(透过现象看本质)的更多相关文章
- (企业面试部分)超详细思路讲解SQL语句的查询实现,及数据的创建。
企业面试部分详细的SQL问题,思路讲解 第一步:创建数据库表,及插入数据信息 --Student(S#,Sname,Sage,Ssex) 学生表 CREATE TABLE student( sno ) ...
- bootstrap datetimepicker 日期插件超详细使用方法
日期时间选择器 目前,bootstrap有两种日历.datepicker和datetimepicker,后者是前者的拓展. Bootstrap日期和时间组件: 使用示例: 从左到右依次是十年视图.年视 ...
- 超详细思路讲解SQL语句的查询实现,及数据的创建。
最近一直在看数据库方面的问题,总结了一下SQL语句,这是部分详细的SQL问题,思路讲解: 第一步:创建数据库表,及插入数据信息 --Student(S#,Sname,Sage,Ssex) 学生表 CR ...
- 超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务
来自:JavaGuide Github 地址:https://github.com/Snailclimb/springboot-integration-examples 目录: 使用 SpringBo ...
- 原生JS插件(超详细)
作为一个前端er,如果不会写一个小插件,都不好意思说自己是混前端界的.写还不能依赖jquery之类的工具库,否则装得不够高端.那么,如何才能装起来让自己看起来逼格更高呢?当然是利用js纯原生的写法啦. ...
- GoJS超详细入门(插件使用无非:引包、初始化、配参数(json)、引数据(json)四步)
GoJS超详细入门(插件使用无非:引包.初始化.配参数(json).引数据(json)四步) 一.总结 一句话总结:插件使用无非:引包.初始化.配参数(json).引数据(json)四步. 1.goj ...
- JAXB常用注解讲解(超详细)
简介: JAXB(Java Architecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例 ...
- Nginx实战部署常用功能演示(超详细版),绝对给力~~~
前言 上次分享了一些开发过程中常用的功能,但如果到真实环境中,其实还需要一些额外的配置,比如说跨域.缓存.配置SSL证书.高可用等,老规矩,还是挑几个平时比较常用的进行演示分享.上篇详见Nginx超详 ...
- Keras代码超详细讲解LSTM实现细节
1.首先我们了解一下keras中的Embedding层:from keras.layers.embeddings import Embedding: Embedding参数如下: 输入尺寸:(batc ...
随机推荐
- (opencv01)读入图像
(opencv01)Opencv之读入图像 读入图像 img = cv2.imread(filepath, flags) filepath : 要读入图片的完整路径 flags : 读入图片的标志 ...
- linux统计nginx日志中请求访问量命令
Nginx 三种分配策略:轮询.权重.ip_hash(比如你登录了一个网站,登录信息已经保存到 a 机器,但当你做后续操作时的请求会到 b 机器,那么就获取不到你原来登录的信息,此时你就需要重新登录了 ...
- unittest系统(八)一文搞定unittest重试功能
在前面的介绍中,我们对unittest进行了分享介绍,那么在实际的应用中,因为客观原因需要对失败,错误的测试用例进行重试,所以呢,现有的unittest的框架无法满足,那么我们可以去改造下是否能够满足 ...
- 记一次GKCTF之旅
GKCTF游记 昨天吧,去GKCTF玩了一下.题目很有意思,宝可梦也很好玩,我心情非常好,天台的风也很大...... 不多说了,把昨天认真看过的几道题记录总结一下.这里特别感谢出题的二进制师傅们,感谢 ...
- for循环排它算法(经典实用)
核心代码 let lis = document.querySelectorAll("li"); for(let i = 0; i < lis.length; i ++) { ...
- C++ //拷贝构造函数调用时机//1.使用一个已经创建完毕的对象来初始化一个新对象 //2.值传递的方式给函数参数传值 //3.值方式返回局部对象
1 //拷贝构造函数调用时机 2 3 4 #include <iostream> 5 using namespace std; 6 7 //1.使用一个已经创建完毕的对象来初始化一个新对象 ...
- OpenStack虚拟网络与物理网络的衔接(flat方式)
by 无若 这边以CentOS7+Liberty版本为例. 过去一段时间(Juno版本之前版本),OpenStack内的虚拟网络与真正的物理网络衔接主要使用openvswitch,其主要问题是在配置网 ...
- GUI编程简介
GUI编程(淘汰) GUI编程怎么学? 这是什么 它怎么玩 该如何去在我们平时运用 class -- 可阅读 组件 窗口 弹窗 面板 文本框 列表框 按钮 图片 监听事件 鼠标 键盘事件 破解工具 1 ...
- erlang学习笔记
安装 Ubuntu Server上: sudo apt-get install erlang 如果安装时下载 太慢,可手工下载deb包( esl-erlang_16.a-rc1_ubuntu_prec ...
- MongoDB-03-分片集群
分片集群(Sharding Cluster) 架构图 规划 10个实例:38017-38026 1 configserver:38018-38020 3台构成的复制集(1主两从,不支持arbiter) ...