前言
本文章不是写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
<code class="dart">class TestPage extends StatelessWidget { final logic = Get.put(TestLogic()); final state = Get.find<TestLogic>().state; @override Widget build(BuildContext context) { return Container(); } }
- logic
<code class="dart">class TestLogic extends GetxController { final TestState state = TestState(); }
- state
<code class="dart">class TestState { TestState() { ///Initialize variables } }
Easy模式
- view
class TestPage extends StatelessWidget { final logic = Get.put(TestLogic()); @override Widget build(BuildContext context) { return Container(); } }
- logic
<code class="dart">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!
<code class="dart">PageView(children: [ FunctionPage(), ExamplePage(), SettingPage(), ])
剖析
咱们能够来剖析下,为什么会产生这种状况,来看下:FunctionPage
<code class="dart">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
<code class="dart">class TestOnePage extends StatelessWidget { final logic = Get.put(TestOneLogic()); @override Widget build(BuildContext context) { return Container(); } }
- logic
<code class="dart">class TestOneLogic extends GetxController { }
Binding:须要配套GetX路由
- binding
<code class="dart">class TestTwoBinding extends Bindings { @override void dependencies() { Get.lazyPut(() => TestTwoLogic()); } }
- view
<code class="dart">class TestTwoPage extends StatelessWidget { final logic = Get.find<TestTwoLogic>(); @override Widget build(BuildContext context) { return Container(); } }
- logic
<code class="dart">class TestTwoLogic extends GetxController { }
- 须要在路由模块绑定下这个binding
<code class="dart">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
<code class="dart">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能够写成空字符:””)
<code class="dart">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); } } } }
- 应用十分的简略
<code class="dart">/// 回收单个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深度分析 | 咱们终将走出本人的路(万字图文)