GetX代碼生成IDEA插件,超詳細功能講解(透過現象看本質)


前言

本文更新非常頻繁,最新內容請查看:最新內容---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-20210919172158224

選中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

還有其它的一些快捷代碼,自行感受嘍~~

Setting功能

隨着功能的不斷增加,一些細分功能,需要在放在設置模塊里了;是時候寫下詳細說明了

image-20210926111944785

lintNorm細分

開啟 lintNorm 功能時,生成的模板代碼是支持倆種庫的:lint 和 flutter_lints

現在對支持做了細分,大家可以隨意設置:支持其中一種庫或者都支持

image-20210926112241600

版本更新說明

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

系列文章 + 相關地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM