前言

本文章不是写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);      }    }  }}
  • 应用十分的简略
/// 回收单个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

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

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

因为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深度分析 | 咱们终将走出本人的路(万字图文)