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

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

作者: 小呆呆666 | 来源:发表于2021-09-07 23:08 被阅读0次

    前言

    本文更新非常频繁,最新内容请查看:最新内容---GetX代码生成IDEA插件功能说明

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

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

    一篇入门使用:Flutter GetX使用---简洁的魅力!

    一篇原理深度剖析:Flutter GetX深度剖析 | 我们终将走出自己的路(万字图文)

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

    img

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

    • 但是随着大量 叼毛 靓仔 给我提的各种需求,这个插件变的已经有点复杂了
    • 尤其是涉及Select Function模块,有些人可能都搞不懂选中的功能按钮是啥意思,就一通全部勾中。。。
    • 所以,本凤雏想详细的,和各位卧龙谈谈这个工具方方面面的功能,希望能帮助各位节省点开发时间

    兄弟们,我实在不想写水文;但是这个工具一个功能按钮,改变的代码可能很少,其背后所蕴含的东西,可能需要大量的笔墨去描述,这边就统一的和各位彦祖于宴亦菲们,说道说道。

    img

    本文长期更新,如果想知道插件每次详细更新内容,可以点进来看。

    代码生成

    • Plugins里搜索getx即可
    image-20210906222922384

    对比

    • 早期代码生成弹框,可选功能比较少,当时还不支持持久化储存
      • 淦,图标也丑
    20210130182809
    • 这是多次完善后的功能选择弹窗
    getx_new

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

    • 首页随着各位靓仔提的各种需求,Select Function,从最初的俩个功能,增加到现在的七个功能

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

      • 方案一:我本来是想做一个折叠收纳区域,次要功能按钮放在折叠区域中
        • 用swing一通写后,发现效果是真的丑,收纳的时候,高度计算也有问题:放弃
      • 方案二:这个是我在翻swing控件的时候,发现了 JBTabbedPane 这个tab控件
        • 效果简洁优雅,完爆折叠思路:采用
    • 这次我全面的改善了dialog布局问题

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

    圖片

    虽然我没试,但是我对自己的代码有信心

    img

    模式选择

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

    来看下区别

    default模式

    image-20210905174923566
    • 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模式

    image-20210905175435395
    • 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

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

    useFolder

    usePrefix

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

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

    usePrefix

    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的注入位置,其它文件不需要变动

    isPageView

    addBinding

    binding是为了统一管理GetXController,来看下binding和非binding的区别

    addBinding

    非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文件有区别
    addLifecycle

    autoDispose

    该功能正如名字一样:自动释放GetXController

    实际上,这是个非常重要的功能,但是实现的太不优雅了,就把它移到了次要功能tab里面了

    GetX内部对回收GetXController,做了很多处理,释放的操作是在GetX路由处理的;但是,业务多变复杂,导致某些GetXController很难被框架自动释放,例如:

    • PageView的子页面
    • 使用GetX封装的复杂组件
    • 不使用GetX路由

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

    通用解决方案

    autoDispose
    • 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这个控件,引入到自己的项目中

    img

    LintNorm

    pub:lint库

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

    img

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

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

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

    • 在生成的模板代码,有几行就会在lint规则下被警告
      • 这俩个注入代码,都会自动推导出对应的类型;但是在lint规则下,会有黄色下划线警告
    image-20210906174811659
    • 需要做这样的调整,才能去掉警告

    [图片上传失败...(image-ecfc0b-1632034686442)]

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

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

    img

    pub:flutter_lints

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

    • 生成了模板代码里面,会有一个警告
    image-20210918222832370
    • 需要做如下调整,才能去掉警告
    image-20210918222957089

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

    template(切换模板命名)

    场景

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

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

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

    • 不重构不行,增加了大量的持久化变量,还全部使用静态变量着实不优雅
    • 增加了数据类,来记录大量重复的持久化数据

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

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

    • 但是使用插件生成对应模块,view模块的Widget可能还是为XxxPage
    • 上面这种情况就不太友好了,你可能需要XxxComponent或者XxxWidget
    • 虽然,可以在设置里重命名后缀名,但是这样可能又对生成Page模块产生影响
    • 所以,这里提供三套模板命名切换,可以快速切换到你需要的自定命名方式

    功能演示

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

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

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

    • 上面的切换,对应设置页面的三套自定义通用后缀
      • 设置页面布局也重写了,看起来更舒服一些,对整体空间利用率也更高了
    image-20210919122822352

    示例

    • mode选择:Easy;Template选择:Component
    image-20210914223626196

    看下代码

    • 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
    • GetBuilder(Auto Dispose)
      • assignId设置为true:GetBuilder就会在页面被回收的时候,自动回收其指定泛型的GetXController
    GetBuilder(Auto Dispose)
    • Obx
      • 说下这里为什么不用箭头符号,如果需要包裹的Widget非常长的话,使用箭头符号后,格式化后的代码并不整齐
      • 考虑到这种情况,所以使用了return形式
    Obx
    • GetX
      • 这个组件我虽然不太喜欢用,但是指不定有喜欢用的小伙伴,就加上了
    GetX
    • 可选择性关闭
    image-20210802160631405

    快捷代码生成

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

    请注意:关键字前缀为getx

    路由模块

    • getxroutepagemap
    getxroutepagemap
    • getxroutename
    getxroutename
    • getxroutepage
    getxroutenpage
    • getxto,getxtoname
    getxto
    • getxoff,getxoffall,getxoffnamed,getxoffallnameed
    getxoff

    依赖注入

    • put
    getxput
    • find
    getxfind
    • lazyPut
    getxlazyput

    业务层

    • GetxController
    getxcontroller
    • getxfinal,getxfinal_
    getxfinal
    • getxget,getxget_
    getxget
    • getset,getset_
    getset

    其它

    • getsnakebar,getdialog,getbottomsheet
    getxdialog
    • getxbuilder,getxobx
    getxobx
    • binding
    getxbinding

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

    版本更新说明

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

    最后

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

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

    img

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

    img

    系列文章 + 相关地址

    相关文章

      网友评论

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

          本文链接:https://www.haomeiwen.com/subject/pxdmwltx.html