This article is updated very frequently, please check the latest content: latest content---GetX code generation IDEA plug-in function description
foreword
This article is not to write the use of the getx framework, but also to explain the function of its code generation IDEA plug-in
I have written two very long getx articles before
An introductory use: Flutter GetX use --- simple charm!
An in-depth analysis of principles: In-depth analysis of Flutter |
Fishing and fishing have already been handed over to you, so there is no need to repeat them.
At the same time, I also wrote a getx code generation plugin: getx_template , this tool is equivalent to a fishing seat (making you more comfortable fishing or eating fish?) Now! The initial function is very simple, that is to generate the corresponding module code for a single page, there is no memory option function, basically it is the level of a plastic seat
- But with a lot of requests from
,, , , this plugin has become a bit complicated. - Especially when it comes to the Select Function module, some people may not understand what the selected function button means, so just tick all of them. . .
- Therefore, I want to talk to you about the functions of this tool in detail, hoping to help you save some development time.
Brothers, I really don't want to write hydrology; but this tool has a function button, and the code may be changed very little, and the things behind it may require a lot of pen and ink to describe. Filipinos, said.
This article has been updated for a long time. If you want to know the details of each update of the plugin, you can click here.
code generation
- Search for getx in Plugins
Compared
Early code generation pop-up box, optional functions are relatively few, and persistent storage was not supported at that time
- gan, the icon is ugly too
- This is the function selection pop-up window after many improvements
I am a very good looking party, I have done a lot of consideration for this latest version of the page
On the home page, with the various needs mentioned by the pretty boys, Select Function has increased from the original two functions to the current seven functions.
- With the increase of function buttons, tiled on the dialog, the height of the entire dialog will become quite long
- The most important thing is: it will make users not clear what the key function buttons in Function are!
Based on the above thinking, I racked my brains to solve this problem
Option 1: I originally wanted to make a folding storage area, and the secondary function buttons were placed in the folding area
- After writing with swing, I found that the effect is really ugly. When storing, there is also a problem with the height calculation: give up
Option 2: This is when I was flipping the swing control and found the tab control JBTabbedPane
- The effect is simple and elegant, and the idea of folding is complete: use
This time I comprehensively improved the dialog layout problem
- The length and width of the entire dialog in the past were written to death, which would cause problems on high-resolution screens.
- This time, I discovered the magic of the pack method (the bitter tears of the swinging dog), the fully refactored interface layout logic
- This time, on a 48-inch screen, the following situation will definitely not occur
I haven't tried it, but I have confidence in my code
Mode selection
Here are two large mode options: default, easy
see the difference
default mode
- 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 Mode
- view
class TestPage extends StatelessWidget {
final logic = Get.put(TestLogic());
@override
Widget build(BuildContext context) {
return Container();
}
}
- logic
class TestLogic extends GetxController {
}
Summary
The above default mode and easy mode, from the code point of view, can still see the obvious difference
- Default mode has one more State layer than Easy mode
- State is specially used to store page variables and initialize related variable data
I have written a more complex module
- There are hundreds of variables on the page (involving complex form submission), and there are dozens of event interactions with users
- A lot of logic in the whole module is calibrated by related variables, and a lot of different data will be initialized. The code of the State layer is almost a thousand lines faster.
- Therefore, when the business is gradually complicated, the State layer is not thin, it supports the logical calibration and reversal of the entire module
Unless it is a business minimal module visible to the naked eye, it is recommended to use the Easy module; otherwise, it is recommended to use the Default mode
main (main function)
useFolder,usePrefix
The functions of useFolder and usePrefix are relatively simple, so they will be discussed together here.
useFolder
This function is selected by default, and a folder will be created in addition to the multiple files created, which is convenient for management
usePrefix
Some friends like to add a prefix to the module name (lowercase + underscore) at each layer: view, state, logic
Here is an optional feature for you
isPageView
Please note: the isPageView and autoDispose buttons cannot be selected at the same time, both of them can solve the problem stored in PageView, select one button, the other button will automatically uncheck
This is a very useful feature
If you use getx in PageView, you may find that GetXController in all subpages is injected all at once! Instead of switching to the corresponding page, inject the corresponding GetXController!
PageView(children: [
FunctionPage(),
ExamplePage(),
SettingPage(),
])
analysis
We can analyze why this happens, let's take a look: FunctionPage
class FunctionPage extends StatelessWidget {
final logic = Get.put(FunctionLogic());
final state = Get.find<FunctionLogic>().state;
@override
Widget build(BuildContext context) {
return Container();
}
}
The step we inject is to place it in the scope of the member variable of the class
- This scope takes effect before instantiating the constructor
- So when we add the instanced Page, the scope of the member variable is triggered directly, and GetXController is injected
PageView trigger mechanism
- PageView triggers the added Widget, which is the build method that triggers the corresponding Widget
- Which Widget to switch to, trigger the build method of the corresponding Widget
With the above understanding, it is easy to solve the problem of PageView
- Just put the injection process in the build method
- Because we are using StatelessWidget, we don't need to consider its refresh problem, it will be refreshed only when its parent node refreshes
- The putIfAbsent method used by GetX to store objects will only store the first injected object, and subsequent objects of the same class will be ignored directly, which can avoid many problems
handles
So this function only needs to change the injection location of GetXController in the View file (other files do not need to be changed)
class FunctionPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final logic = Get.put(FunctionLogic());
final state = Get.find<FunctionLogic>().state;
return Container();
}
}
- Compared
addBinding
Binding is for unified management of GetXController, let's see the difference between binding and non-binding
Non-Binding
- view
class TestOnePage extends StatelessWidget {
final logic = Get.put(TestOneLogic());
@override
Widget build(BuildContext context) {
return Container();
}
}
- logic
class TestOneLogic extends GetxController {
}
Binding: Requires matching GetX route
- 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 {
}
- This binding needs to be bound in the routing module
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(),
),
];
}
Summarize
In the binding file, lazy injection is used: when the find method is used, the real injection is made
So in the view, you need to change the put to find.
- Add binding files and use lazy injection
- view file, put change to find
- You need to bind the binding instance on the corresponding page in the getx routing module
minor (minor feature)
addLifecycle
This is a very simple function, placed under the secondary functions tab
For some friends, the logic module needs to write the onReady and onClose callbacks often, and they are too lazy to write it every time; so the function of automatically supplementing these two callbacks has been added to the plug-in.
- Only the Logic files are different. When this function is enabled: the onReady() and onClose() methods are automatically added
class TestLogic extends GetxController {
final TestState state = TestState();
@override
void onReady() {
// TODO: implement onReady
super.onReady();
}
@override
void onClose() {
// TODO: implement onClose
super.onClose();
}
}
- Compared
autoDispose
The function is exactly what the name says: auto-release GetXController; in fact, this is a very important function, but the implementation is too inelegant, so I moved it to the secondary function tab
When we use GetX, there may be no feeling that GetxController has not been released. This situation is because we generally use the set of route jump APIs (Get.to, Get.toName...) of getx. Class: To use Get.toName, you must use GetPage; if you use Get.to, you do not need to register in GetPage. There is an operation added to GetPageRoute inside Get.to
It can be seen from the above registration in GetPage, which means that when we jump to the page, GetX will store the page information and manage it. The following two scenarios will cause GetxController to fail to release.
Conditions under which GetxController can be automatically released
- GetPage+Get.toName is used together and can be released
- Use Get.to directly, releasable
GetxController cannot be automatically released scene
- Do not use the routing jump provided by GetX: directly use the jump operation of the native routing api
- This will directly cause GetX to be unable to perceive the life cycle of the corresponding page GetxController, which will cause it to fail to release
Navigator.push(
context,
MaterialPageRoute(builder: (context) => XxxxPage()),
);
From this, it can be seen from the above that GetxController cannot be released: Do not use GetX routing
optimal solution
There is an optimal solution. Even if you don't use Getx routing, you can easily recycle GetXController on each page. Thanks to @法 for pointing out in the comments
- Manually make getx aware of routes
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage,
///此处配置下!
navigatorObservers: [GetXRouterObserver()],
);
}
}
///自定义这个关键类!!!!!!
class GetXRouterObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
RouterReportManager.reportCurrentRoute(route);
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) async {
RouterReportManager.reportRouteDispose(route);
}
}
To be honest, this principle is actually very simple, but the idea is very interesting; if you click on the two methods of reportCurrentRoute
and reportRouteDispose
, you probably know what is going on.
reportCurrentRoute
is to assign the current route to GetX- When we enter a page, the corresponding GetXController will be initialized, and finally the
_startController<S>({String? tag})
method will be called _startController
will callRouterReportManager.appendRouteByCreate(i)
to save the injected GetXController- Saved in a map, the key is the current route
route
, the value is HashSet, multiple GetXControllers can be saved - ok, when the route is closed, it is recycled in the
reportRouteDispose
method, the key is the currentroute
, and it traverses all the GetXController recycling in the value - I giao, based on this kind of thinking, everyone can do a lot of things! ! !
StatefulWidget scheme
If the above optimal solution can't help you solve the problem of GetXController recycling, you may encounter a special scenario. Generally speaking, you can basically analyze your own code by analyzing it.
If you are too lazy to analyze the reasons, try the following compromise solution; the granularity is extremely small, and it is solved for the single page dimension
Here I simulated the above scenario and wrote a solution
- Jump to the first page
Navigator.push(
Get.context,
MaterialPageRoute(builder: (context) => AutoDisposePage()),
);
demo page
- This place must use StatefulWidget, because in this case, the life cycle cannot be sensed, and the StatefulWidget life cycle needs to be used
- At the dispose callback, delete the current GetxController from the entire GetxController management chain
class AutoDisposePage extends StatefulWidget {
@override
_AutoDisposePageState createState() => _AutoDisposePageState();
}
class _AutoDisposePageState extends State<AutoDisposePage> {
final AutoDisposeLogic logic = Get.put(AutoDisposeLogic());
@override
Widget build(BuildContext context) {
return BaseScaffold(
appBar: AppBar(title: const Text('计数器-自动释放')),
body: Center(
child: Obx(
() => Text('点击了 ${logic.count.value} 次',
style: TextStyle(fontSize: 30.0)),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => logic.increase(),
child: const Icon(Icons.add),
),
);
}
@override
void dispose() {
Get.delete<AutoDisposeLogic>();
super.dispose();
}
}
class AutoDisposeLogic extends GetxController {
var count = 0.obs;
///自增
void increase() => ++count;
}
Seeing this, you might think, ahhh! Why is it so troublesome, why do I have to write StatefulWidget, so troublesome!
n't worry, I also thought of this problem, I specially added the function of automatic recycling to the
If the page you write cannot be recycled, remember to check autoDispose
- How to judge whether the GetxController of the page can be recycled? It's actually very simple. The unreleased scene above has been described more clearly. If you're not sure, just look at it again.
Let's take a look at the code, the default mode is the same
- view
class AutoDisposePage extends StatefulWidget {
@override
_AutoDisposePageState createState() => _AutoDisposePageState();
}
class _AutoDisposePageState extends State<AutoDisposePage> {
final AutoDisposeLogic logic = Get.put(AutoDisposeLogic());
@override
Widget build(BuildContext context) {
return Container();
}
@override
void dispose() {
Get.delete<AutoDisposeLogic>();
super.dispose();
}
}
- logic
class AutoDisposeLogic extends GetxController {
}
Optimize StatefulWidget scheme
The above is a general solution, you don't need to introduce anything else; but this solution uses StatefulWidget, and the code is a lot more, which makes me a little nervous
I have quite an obsessive-compulsive disorder. After thinking about it for a long time, starting from the outside, I wrote a general control to recycle the corresponding GetXController
GetBindWidget
- The meaning of this control: Bind GetXController to the life cycle of the current page, and automatically recycle when the page is closed
- The control can recycle a single GetXController (bind parameter), and can add the corresponding tag (tag parameter); it can also recycle multiple GetXController (binds), and can add multiple tags (tags parameter, please correspond to binds one by one; no tag In GetXController, tag can be written as null character: "")
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 = Get.put(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);
}
}
}
}
- very simple to use
/// 回收单个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
LintNorm
pub: lint library
This function, at first glance, everyone is probably confused; if I hadn't written this, I would have been confused when I saw it.
However, this function is really the gospel for a small number of obsessive-compulsive patients
Because the author of getx, in the demo project, introduced the lint library, some small partners may also use this library
lint is a code base with strict rules. For the corresponding irregularities of the code, IDEA will give prompts; for many codes that we think are reasonable, sometimes corresponding warnings may also be given.
In the generated template code, a few lines will be warned under the lint rule
- These two injection codes will automatically deduce the corresponding type; but under the lint rule, there will be a yellow underline warning
- This adjustment is required to get rid of the warning
Selecting the lintNorm button will generate template code in the following form; so this function is a gospel for obsessive-compulsive patients. . .
For people who use strong rules like lint, I say:
pub:flutter_lints
Recently, Flutter has added the flutter_lints library by default in new projects. The rules of this library are much looser, and the rules are basically standard flutter writing.
- In the generated template code, there will be a warning
- The following adjustments need to be made to remove the warning
When you turn on lintNorm, it will also help you make up the constructor of the generated page
template (toggle template naming)
Scenes
This function provides the operation of switching template naming
Three sets of template names are provided, only three sets are provided, and there will be no more
Internally refactored the persistence module
- No refactoring will work, adding a large number of persistent variables, and using all static variables is really inelegant
- Added data classes to record a large number of repeated persistent data
Why does provide the function of switching template naming?
When the business becomes more and more complex, in many cases, complex general components can also be encapsulated by getx
- But use the plugin to generate the corresponding module, the Widget of the view module may still be XxxPage
- The above situation is not very friendly, you may need XxxComponent or XxxWidget
- Although, you can rename the suffix name in the settings, but this may have an impact on the generation of the Page module
- Therefore, here are three sets of template naming switches, you can quickly switch to the custom naming method you need
Demo
plugin window adds three sets of template switching
Select Template to provide three sets of switching template names: Page, Component, Custom
- Default Page
Three sets of template names support custom modification
The above switch corresponds to the three sets of custom general suffixes on the settings page
- The setting page layout has also been rewritten, it looks more comfortable, and the overall space utilization rate is also higher
example
- Mode selection: Easy; Template selection: Component
look at the code
- view
class TestComponent extends StatelessWidget {
final logic = Get.put(TestLogic());
@override
Widget build(BuildContext context) {
return Container();
}
}
- logic
class TestLogic extends GetxController {
}
Wrap Widget
This is a very useful feature
Currently four types of Wrap Widgets are supported: GetBuilder, GetBuilder(autoDispose), Obx, GetX
Precautions for use: click the mouse on the Widget, then press alt+enter; do not double-click to select the widget name
- GetBuilder
GetBuilder(Auto Dispose)
- assignId is set to true: GetBuilder will automatically recycle its specified generic GetXController when the page is recycled
Obx
- Say why the arrow symbol is not used here. If the Widget to be wrapped is very long, after using the arrow symbol, the formatted code is not neat
- Considering this situation, the return form is used
GetX
- Although I don't like to use this component, but maybe there are friends who like to use it, so I added it
- optional close
Quick code generation
The plugin also provides you with the function of entering keywords to generate shortcut code snippets
Note: Keyword prefix is getx
routing module
- getxroutepagemap
- getxroutename
- getxroutepage
- getxto,getxtoname
- getxoff,getxoffall,getxoffnamed,getxoffallnameed
dependency injection
- put
- find
- lazyPut
Business Layer
- GetxController
- getxfinal,getxfinal_
- getxget,getxget_
- getset,getset_
other
- getsnakebar,getdialog,getbottomsheet
- getxbuilder,getxobx
- binding
There are some other shortcut codes, feel it yourself~~
Setting function
With the continuous increase of functions, some subdivided functions need to be placed in the setting module, and it is time to write down the detailed description.
lintNorm subdivision
- When the lintNorm function is enabled, the generated template code supports two libraries: lint and flutter_lints
- Now the support is subdivided, and you can set it at will: support one of the libraries or support both
useFolderSuffix
This function can choose whether to use the folder suffix or not, and it is turned off by default.
when useFolderSuffix is not selected
- Settings page
- The generated folder is like this
when useFolderSuffix is selected
- Settings page
- Append suffix to folder
- The added suffix is the ViewName field in each Template
Version update instructions
3.2.x
- Added template switching function, greatly optimized the internal persistence method
- Refactoring settings page layout
- Support flutter_lints rules
- Split lintNorm: lint and flutter_lints (custom support in settings)
- Fix Template title missing (thanks to @ for helping test)
- Add useFolderSuffix function in settings
3.1.x
Significantly improved overall page layout
- There will be no more pit ratio problems on high-size screens
- Support for lint rules (lintNorm)
Improve the quick code prompt function, the "get" prefix is changed to "getx"
- The prefix
get
will make the prompt code overwhelmed by many system codes. After changing togetx
, it can be seen at a glance
- The prefix
- Plugin description page, add a link to this article
3.0.x
- Migrate project code from Java to kotlin
- ModuleName input: the first letter is lowercase, and the interior will be automatically marked as uppercase
- Increase the generation of a large number of shortcut code snippets
- Plug-in project logic refactoring, separation of interface layer and logic layer
Wrap Widget added: GetBuilder (Auto Dispose)
- The corresponding GetXController can be automatically recycled
- Add PageView solution
- fix some bugs
2.1.x
- Major update!
- Add Wrap Widget: GetBuilder, Obx, GetX
- Increase the generation of shortcut code snippets
- Greatly optimized plugin layout
- Add the perfect life cycle callback function (addLifecycle)
- Add binding function (addBinding)
1.5.x
- Added memory function (button for memory selection)
- Add GetXController automatic recycling function (autoDispose)
- Support for modifying common suffixes: view, logic, state
- Adjust the plugin description and fix some bugs
1.3.x
- Adapt to multiple versions of IDEA (only one IDEA version was adapted before, pit)
- Add plugin logo
- Add a getx English article (machine translation of your own blog article)
- Improve plugin description
1.2
- Adjust the description content
1.1
- Fixed the problem of abnormal package import when adding prefix
1.0
- You can use this plugin to generate a lot of getx framework code
- This can greatly improve your efficiency
- If you have any questions, please send me an issue; before you do: please think about it first, is it reasonable
At last
When I continue to improve this plugin, it is also a process I keep thinking about.
Thank you all for your various needs
This plug-in can be improved a little bit, so that now, it can really help the pretty boys save a little development time
series of articles + related address
- Github address of the plugin: getx_template
- Flutter GetX use --- Simple charm!
- Flutter GetX in-depth analysis | We will eventually find our way (4D text)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。