preface

One of the things that really bothered me so far with Bloc was that there was no real cross-page interaction! After repeated visits to official documents, a “pseudo-” cross-page interaction is achieved using a global Bloc approach, which can be seen in detail: Flutter_bloc parsing; Fish_redux’s broadcast mechanism is perfect for cross-page interaction, and I wrote a 10,000-word article on how to use the framework: Fish_redux: For small and medium projects, fish_redux can reduce development efficiency to some extent. Recently, I tried GetX related functions, which solved a lot of my pain points

After writing the whole article, I immediately replaced all Bloc codes in one of my demos with GetX and removed the Fluro frame. It feels like using Getx saves a lot of template code, but it’s still a bit repetitive: create folders, create a few required files, and write initialization code and classes that have to be written. It was a little tedious, so I spent some time writing a plugin for GetX in order to make it easier for me to develop! The above repeated code, files, folders can all be generated in one click!

GetX related advantages

  • Dependency injection
    • GetX stores XxxGetxController via dependency injection. We’ve moved away from the inherent itedWidget playbook and manually managed the instances ourselves, expanding the usage scenarios
    • Simple thinking, but profound: elegant cross-page functionality is based on this design, getting instances without BuildContext, GetBuilder automated processing and reduced input arguments, and so on
  • Cross-page interaction
    • This is definitely an advantage of GetX! Cross-page interaction scenarios are all too common in complex production environments, and GetX’s cross-page interaction is almost as simple as Fish_redux’s
  • Routing management
    • Yes, getX implements route management internally, and it’s easy to use! Bloc did not implement route management, so I had to find a routing management framework with high star volume, so I chose Fluro. However, I have to say that fluro is really a torture to use. Every time I create a new page, what I resist most is to write the Fluro routing code, which goes back and forth across several files
    • GetX implements dynamic route parameter transfer, that is, write parameters to the named route and get the parameters spelled on the route. That is, write H5 with flutter and transfer values directly to the Url. It’s time to ditch the complex Fluro without thinking
  • Global BuildContext is implemented
  • Internationalization, subject realization

Getx’s cross-page functionality is very elegant.

Down will be a comprehensive introduction to the use of GetX, the article is not divided into water reading quantity, and strive to write a clear, convenient for everyone to refer to at any time

To prepare

The introduction of

  • Start by importing the GetX plug-in
# getx status management framework HTTPS://pub.flutter-io.cn/packages/get# The last version of non-air safety (Flutter2.0Previous version)get: ^3.26. 0For the latest version, please check HTTPS://pub.flutter-io.cn/packages/get
get: ^4.13.
Copy the code

GetX address

  • Making: jonataslaw/getx
  • The Pub: the get

Main entrance configuration

  • Just need toMaterialApptoGetMaterialAppCan be
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnGetMaterialApp( home: CounterGetPage(), ); }}Copy the code
  • Each module guide package, all use the bread
import 'package:get/get.dart';
Copy the code

The plug-in

Make fun of the process of writing plug-ins, actually write this template code to generate plug-ins, in fact, it is not difficult, there are many people on the Internet to write examples, reference ideas, can quickly come out, is some configuration comparison cone.

We recommend using Gradle mode to develop plugins, and bala bala bala lists a bunch of benefits. After much consideration, I decided to rewrite it in Gradle mode.

If you want to create a new Gradle project, you can download Gradle from your own site. If you want to create a new Gradle project, you can download Gradle from your own site. If you want to create a new Gradle project, you can download Gradle from your own site. There is a big BUG! Java folder will not be automatically generated in main folder! New -> Plugin DevKit does not have Action options, almost persuaded me to quit, changed the IDEA of seven or eight versions tried not to work! The Action option does not appear. After two days, I accidentally created a Java file under the main folder. Then I right-click the Java file: New -> Plugin DevKit, and the Action option appears. What a few buddhas…

There is a huge dent problem, developing a plug-in in Gradle mode, put the template code files in the main file under, under the SRC, under the root directory, take less than the inside of the file content, this is really changed me a lot of time, searched many blogs, have found that the problem didn’t write, the official document example seen it a few times also didn’t find what, Later, I found a project from three years ago, looked through the code and found that all resource files must be placed in the Resources folder to read the file contents… Damn it

instructions

  • Plug-in address
    • Making: getx_template
    • Jetbrains: getx_template
  • Plug-in effect
    • Take a look at an image of the plugin using the fish_redux plugin style
    • There are some optional functions, so make it multi-button style, and you can do it according to your own needs

Function description of the plug-in

  • Model: Generate GetX pattern
    • Default: generates three files: state, logic, and View
    • Easy: simple mode, generate two files: Logic, view
  • Function: Function selection
    • UseFolder: Use a file. After selecting a folder, a folder is generated. The name of a large hump is automatically changed to lowercase and underscore
    • UsePrefix: a prefix is added to the generated file. The prefix is “big hump”. The name is automatically changed to “lowercase” and “underscore”
    • AutoDispose: If you find that a page cannot automatically dispose GetxController, you can turn on this feature. Please refer to how to dispose GetxController automatically. In normal cases, this function is not required
    • AddLifecycle: Automatically adds the lifecycle callback method in GetXController, enabled on demand
    • AddBinding: Automatically adds a binding file
      • If you know what binding is, do you recommend turning it on
      • If you do not understand the concept and function of binding, it is not recommended to enable it. Not using binding does not affect development
  • Module Name: The Name of the Module. Please use a large hump. Uppercase letters are supported

rendering

  • Generate template code

  • It also supports persistence

  • Alt + Enter: You can choose to wrap widgets, there are four options: GetBuilder, GetBuilder (Auto Dispose), Obx, GetX, greatly convenient development YO (^ U ^) Blue ~YO
    • If you find a page that your GetXController cannot Dispose, use GetBuilder (Auto Dispose) to Wrap your Widget

  • Quick snippet tip: I wrote a lot of them myself, but also some direct quotes:getx-snippets-intelliJ
    • Typing the get prefix prompts you

The installation

  • In your Settings select Plugins — type “getx” — select “GeX” — then install — then click “Apply”
  • If there is any problem when using this plug-in, please send me an issue on github of the project, and I will deal with it as soon as possible after I see it

counter

rendering

  • Experience the

implementation

The first, of course, is to implement a simple counter. How does GetX decouple the logical layer from the interface layer

  • To generate a simple file using the plug-in
    • Mode selection: Easy
    • Function Selection: useFolder

Take a look at the generated default code, which is very simple and explained in detail in two state managers

  • logic
import 'package:get/get.dart';

class CounterGetLogic extends GetxController {}Copy the code
  • view
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'logic.dart';

class CounterGetPage extends StatelessWidget {
  final CounterGetLogic logic = Get.put(CounterGetLogic());

  @override
  Widget build(BuildContext context) {
    returnContainer(); }}Copy the code

Responsive state management

When the data source changes, methods to refresh the component are performed automatically

  • Logic layer
    • This layer is used because it deals with page logic and because the word “Controller” is too long to confuse Flutter with some of the control controllers that come with the FlutterlogicThe end, that’s what we’re going to do herelogicLayer, of course, depending on what you want to do, Event, Controller
    • Here I write the variable after the value.obsOperation, is defined as a responsive variable, when the variable value changes, the page refresh method will automatically refresh; Base types, lists, classes can be added.obsTo make it a responsive variable
class CounterGetLogic extends GetxController {
  var count = 0.obs;

  ///Since the increase
  void increase() => ++count;
}
Copy the code
  • The view layer

    • WTF, why is the operation of the Logic layer in the build method? Tease me? ——— No, the STL is stateless, which means it will not be reorganized twice, so the instance operation will only be executed once, and the Obx() method can refresh the component, which is the perfect solution to refresh the component
class CounterGetPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    CounterGetLogic logic = Get.put(CounterGetLogic());

    return Scaffold(
      appBar: AppBar(title: const Text('GetX counter ')),
      body: Center(
        child: Obx(
          () => Text('click on the${logic.count.value}Time ',
              style: TextStyle(fontSize: 30.0)),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: constIcon(Icons.add), ), ); }}Copy the code
  • Of course, you could write it this way
class CounterGetPage extends StatelessWidget {
  final CounterGetLogic logic = Get.put(CounterGetLogic());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GetX counter ')),
      body: Center(
        child: Obx(
          () => Text('click on the${logic.count.value}Time ',
              style: TextStyle(fontSize: 30.0)),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: constIcon(Icons.add), ), ); }}Copy the code
  • You’ll find that the way to refresh a component is extremely simple: Obx(), which makes it fun to write fixed-point refresh operations around

  • Conditions for the Obx() method to refresh

    • The refresh operation is performed only when the value of a responsive variable changes. When a variable is initially set to test and then set to test, the refresh operation is not performed
    • When you define a reactive variable and it changes, the Obx() method that wraps the reactive variable does not refresh. Cool!
  • How do you update an entire class object if you set it as a response?

    • The following explanation comes from the official README document
    • So this is an attempt to set the whole class object to the response type, and when you change one of the variables of the class, and then you do the update,Any Obx() that wraps the response class variable is refreshedTo set the response type of the entire class, which needs to be used in actual scenarios
// model
// We will make the entire class observable instead of each attribute.
class User{
    User({this.name = ' '.this.age = 0});
    String name;
    int age;
}

// controller
final user = User().obs;
// When you need to update the user variable.
user.update( (user) { // This parameter is the class itself that you want to update.
    user.name = 'Jonny';
    user.age = 18;
});
// Another way to update the user variable.
user(User(name: 'João', age: 35));

// view
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"));
// You can also access model values without using.value.
user().name; // Note that this is a user variable, not a class variable (the first letter is lowercase).
Copy the code

Simple State Management

GetBuilder: This is an extremely lightweight state manager that uses very few resources!

  • Logic: Let’s start with the Logic layer
class CounterEasyGetLogic extends GetxController {
  var count = 0;

  voidincrease() { ++count; update(); }}Copy the code
  • view
class CounterEasyGetPage extends StatelessWidget {
  final CounterEasyGetLogic logic = Get.put(CounterEasyGetLogic());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter - Simple')),
      body: Center(
        child: GetBuilder<CounterEasyGetLogic>(
          builder: (logicGet) => Text(
            'click on the${logicGet.count}Time ',
            style: TextStyle(fontSize: 30.0),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: constIcon(Icons.add), ), ); }}Copy the code
  • Analysis: GetBuilder method
    • Init: Although the above code is useless, this parameter is present in GetBuilder because it is used when loading variablesGet.put()To generate theCounterEasyGetLogicObject, which GetBuilder automatically looks up, so you don’t need to use the init parameter
    • Builder: Method parameter that has an input parameter and the type is the type of the generic passed in by GetBuilder
    • InitState, Dispose, etc. : GetBuilder has all periodic callbacks to the StatefulWidget and can do something within the corresponding callbacks

conclusion

Analysis of the

  • Obx is used with Rx reactive variables and GetBuilder is used with UPDATE: note that this is exactly the same scheme for both fixed-point refresh controls
    • Difference: the former response variable change, Obx automatic refresh; The latter requires a manual refresh call using update
  • Reactive variable, because we’re usingStreamBuilder, will consume some resources
  • GetBuilderThe interior is essentially a wrapper around the StatefulWidget, so the footprint is minimal

Usage scenarios

  • In general, you can use reactive variables for most scenarios
  • However, using reactive variables in a List containing a large number of objects will generate a large number ofGetStreamIn this case, simple state management should be considered
  • In summary: The combination of GetBuilder and Update is recommended
    • GetBuilder has a built-in function to recycle GetxController, which can avoid some of the problems of automatic recycling GetxController
      • AssignId: true for GetBuilder; Or use plugins one-click Wrap Widget: GetBuilder (Auto Dispose)
    • Using Obx, the initialization of related variable definitions and entity updates are different from the usual way of writing them, which can be quite confusing for people who are new to the framework
    • The GetX plugin now supports one-click Wrap Widget generation of GetBuilder, which can improve your development efficiency to some extent

Cross-page interaction

Cross-page interaction is a very important feature in complex scenarios. How does GetX implement cross-page event interaction

rendering

  • Experience the
  • Cool, this is true cross-page interaction! Subordinate pages can call events of superior pages at will, and data will be reset naturally next time the page is closed (global Bloc will not be reset, manual reset is required)

implementation

A page

Regular code

  • logic
    • The increment event here is called by another page, which is not used by the page itself
class GetJumpOneLogic extends GetxController {
  var count = 0;

  ///Jump to cross page
  void toJumpTwo() {
    Get.toNamed(RouteConfig.getJumpTwo, arguments: {'msg': 'I'm the data from the last page.'});
  }

  ///Jump to cross page
  voidincrease() { count = ++count; update(); }}Copy the code
  • view
    • Here is a text display and jump function
class GetJumpOnePage extends StatelessWidget {
  /// Use get.put () to instantiate your class to make it available to all child routes at the moment.
  final GetJumpOneLogic logic = Get.put(GetJumpOneLogic());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(title: Text('Cross-page -One')),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.toJumpTwo(),
        child: const Icon(Icons.arrow_forward_outlined),
      ),
      body: Center(
        child: GetBuilder<GetJumpOneLogic>(
          builder: (logic) {
            return Text('Cross the page -Two clicks away${logic.count}Time ',
                style: TextStyle(fontSize: 30.0)); },),),); }}Copy the code

Page 2

This page is the point

  • logic
    • Will show how to call the event of the previous page
    • How to receive last page data
    • Please note that,GetxControllerContains more complete lifecycle callbacks that can be found inonInit()Accept the passed data; If the received data needs to be refreshed on the page, clickonReadyThe callback receives data operations,onReadyIs in theaddPostFrameCallbackThe refresh data operation is called in the callbackonReadyTo ensure that the page is refreshed after the initial loading is complete
class GetJumpTwoLogic extends GetxController {
  var count = 0;
  var msg = ' ';

  @override
  void onReady() {
    var map = Get.arguments;
    msg = map['msg'];
    update();

    super.onReady();
  }

  ///Jump to cross page
  voidincrease() { count = ++count; update(); }}Copy the code
  • view
    • Plus click event, click, can realize the transformation of two pages of data
    • Here we go. Here we goGet.find(), get the previous instantiation GetXController, get a module GetXController is very easy to do, you can use this GetXController to call the corresponding event, also can use it, get the module data!
class GetJumpTwoPage extends StatelessWidget {
  final oneLogic = Get.find<GetJumpOneLogic>();
  final GetJumpTwoLogic twoLogic = Get.put(GetJumpTwoLogic());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(title: Text('Cross-page -Two')),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          oneLogic.increase();
          twoLogic.increase();
        },
        child: const Icon(Icons.add),
      ),
      body: Center(
        child: Column(mainAxisSize: MainAxisSize.min, children: [
          // Count display
          GetBuilder<GetJumpTwoLogic>(
            builder: (logic) {
              return Text('Cross the page -Two clicks away${twoLogic.count}Time ',
                  style: TextStyle(fontSize: 30.0)); },),// Pass data
          GetBuilder<GetJumpTwoLogic>(
            builder: (logic) {
              return Text('Data passed:${twoLogic.msg}',
                  style: TextStyle(fontSize: 30.0)); },),],),); }}Copy the code

conclusion

GetX cross-page interaction events are very simple and very low intrusive. There is no need to configure anything in the main entrance. In complex business scenarios, such a simple cross-page interaction can achieve a lot of things

Advanced! counter

We may encounter many complex business scenarios, in complex business scenarios, just a module about the initialization of variables may be very much, at this time, if will also state (state) and logic (logical) write together, maintain may see more dizzy, there will be a state and logic layer separation, This way GetX can be used in slightly larger projects with a clean structure!

Continue with the counter example here!

implementation

There are three structures: State, Logic, and View.

  • Here the template code is generated using the plug-in
    • Model: Select Default
    • Function: useFolder (default)

Take a look at the generated template code

  • state
class CounterHighGetState {
  CounterHighGetState() {
    ///Initialize variables}}Copy the code
  • logic
import 'package:get/get.dart';

import 'state.dart';

class CounterHighGetLogic extends GetxController {
  final state = CounterHighGetState();
}
Copy the code
  • view
import 'package:flutter/material.dart';
import 'package:get/get.dart';

import 'logic.dart';
import 'state.dart';

class CounterHighGetPage extends StatelessWidget {
  final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
  final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;

  @override
  Widget build(BuildContext context) {
    returnContainer(); }}Copy the code

Why are these three modules written and State needs to be mentioned separately? Please browse below quickly

transform

  • state
    • You can see here that the count type usesRxInt, no usevarThe reason for using this variable type is to initialize all operations in the constructor if used directlyvarYou can’t derive it if you don’t assign it immediatelyRxType, so this is defined directly asRxIntThe base type capitalizes the initial letter and then addsRxprefix
    • Actually directly usingvarThis is also possible, however, when using the response variable.valueCan’t prompt, need to write by hand, so still honestly state the specific type of Rx
    • More details: Declare reactive variables
class GetCounterHighState {
  late int count;

  GetCounterHighState() {
    count = 0; }}Copy the code
  • logic
    • The logical layer is simpler, but note that the state class needs to be instantiated to begin with
class GetCounterHighLogic extends GetxController {
  final state = GetCounterHighState();

  ///Since the increase
  voidincrease() { state.count = ++state.count; update(); }}Copy the code
  • view
    • In fact, the View layer is almost the same as the previous one, except that the state layer is separate
    • becauseCounterHighGetLogicInstantiated, so directly usedGet.find<CounterHighGetLogic>()You get the logic layer that you just instantiated, and then you get state, which you receive with a separate variable
    • Ok, at this point: Logic only focuses on triggering event interactions, and state only focuses on data
class GetCounterHighPage extends StatelessWidget {
  final GetCounterHighLogic logic = Get.put(GetCounterHighLogic());
  final GetCounterHighState state = Get.find<GetCounterHighLogic>().state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter - Advanced edition')),
      body: Center(
        child: GetBuilder<GetCounterHighLogic>(
          builder: (logic) {
            return Text(
              'click on the${state.count}Time ',
              style: TextStyle(fontSize: 30.0)); }, ), ), floatingActionButton: FloatingActionButton( onPressed: () => logic.increase(), child: Icon(Icons.add), ), ); }}Copy the code

contrast

After seeing the above transformation, you may want to ridicule the screen: Pit bi ah, before the simple logic layer, was split into two, still make so much trouble, are you invited by the monkey funny than?

Before you make fun of it, the state layer will also maintain a lot of things when the business is too complex. Let’s take a look at the following example: the following code will not run directly. For details, please check the project: flutter_use

  • state
class MainState {
  ///Choose the index
  late int selectedIndex;

  ///Control whether to expand
  late bool isUnfold;

  ///Whether the zoom
  late bool isScale;

  ///Classification button data source
  late List<BtnInfo> list;

  ///Item information for Navigation
  late List<BtnInfo> itemList;

  ///PageView page
  late List<Widget> pageList;
  late PageController pageController;

  MainState() {
    // Initialize index
    selectedIndex = 0;
    // Not expanded by default
    isUnfold = false;
    // No scaling by default
    isScale = false;
    / / PageView page
    pageList = [
      KeepAlivePage(FunctionPage()),
      KeepAlivePage(ExamplePage()),
      KeepAlivePage(SettingPage()),
    ];
    / / item columns
    itemList = [
      BtnInfo(
        title: "Function",
        icon: Icon(Icons.bubble_chart),
      ),
      BtnInfo(
        title: "Paradigm",
        icon: Icon(Icons.opacity),
      ),
      BtnInfo(
        title: "Settings",
        icon: Icon(Icons.settings),
      ),
    ];
    // Page controllerpageController = PageController(); }}Copy the code
  • logic
class MainLogic extends GetxController {
  final state = MainState();

  @override
  void onInit() {
    ///Example Initialize the application information
    InitConfig.initApp(Get.context);
    super.onInit();
  }

  ///Switch the TAB
  void switchTap(int index) {
    state.selectedIndex = index;
    state.pageController.jumpToPage(index);
    update();
  }

  ///Whether to expand the sidebar
  void onUnfold(boolisUnfold) { state.isUnfold = ! state.isUnfold; update(); }///Whether the zoom
  void onScale(boolisScale) { state.isScale = ! state.isScale; update(); initWindow(scale: isScale ?1.25 : 1.0); }}Copy the code
  • view
class MainPage extends StatelessWidget {
  final MainLogic logic = Get.put(MainLogic());
  final MainState state = Get.find<MainLogic>().state;

  @override
  Widget build(BuildContext context) {
    return BaseScaffold(
      backgroundColor: Colors.white,
      body: Row(children: [
        ///The sidebar area
        GetBuilder<MainLogic>(
          builder: (logic) {
            return SideNavigation(
              selectedIndex: state.selectedIndex,
              isUnfold: state.isUnfold,
              isScale: state.isScale,
              sideItems: state.itemList,
              / / click on the item
              onItem: (index) => logic.switchTap(index),
              // Expand the sidebar
              onUnfold: (isUnfold) => logic.onUnfold(isUnfold),
              // Scale the overall layoutonScale: (isScale) => logic.onScale(isScale), ); },),///Expanded took up the remaining spaceExpanded( child: PageView.builder( physics: NeverScrollableScrollPhysics(), itemCount: state.pageList.length, itemBuilder: (context, index) => state.pageList[index], controller: state.pageController, ), ) ]), ); }}Copy the code

As can be seen from the above, there are a lot of states in the State layer. When some modules involve a lot of: submit form data, jump data, display data, etc., the code in the State layer will be quite a lot, believe me, really a lot. Once the business changes, there will be frequent maintenance and modification

In complex businesses, it is always wise to separate the state layer from the business logic layer

The last

  • The effect of the module is not put, and the above counter effect is exactly the same, want to experience, you can click: experience
  • Simple business module, can use two layer structure: Logic, view; For complex service modules, it is recommended to use a three-tier structure: State, Logic, and View

The use of the Binding

instructions

As you may have noticed, addBinding has been added to the plugin

In addition, getX’s demo also uses binding, so you will want to use this feature very much

What this function actually does is very simple

  • Manages GetXController used by a single module in a unified manner
  • The binding module needs to be bound on the getX routing page; When entering the page, uniformly inject the GetXController of the Binding module

It certainly has its advantages

  • Multiple GetXControllers for complex modules can be managed uniformly

Please note that

  • Binding is not recommended in the get.to () method
    • If there are multiple pages that jump to an existing binding page, each of your get.to () methods needs to be bound
    • This is extremely easy to bug, on the back of the disc, very unfriendly
  • With Binding, you should use getX’s named route

For the record: not using binding does not affect functionality

use

The routing module must be built first

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnGetMaterialApp( initialRoute: RouteConfig.testOne, getPages: RouteConfig.getPages, ); }}class RouteConfig {
  static const String testOne = "/testOne";
  static const String testTwo = "/testOne/testOne";

  static final List<GetPage> getPages = [
    GetPage(
      name: testOne,
      page: () => TestOnePage(),
      binding: TestOneBinding(),
    ),
    GetPage(
      name: testTwo,
      page: () => TestTwoPage(),
      binding: TestTwoBinding(),
    ),
  ];
}
Copy the code

Create page module

  • Select the addBinding function

  • Create a TestOne
///Logic layer
class TestOneLogic extends GetxController {
  void jump() => Get.toNamed(RouteConfig.testTwo);
}

///The view layer
class TestOnePage extends StatelessWidget {
  final logic = Get.find<TestOneLogic>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Page one')),
      body: Center(child: Text('Page one', style: TextStyle(fontSize: 30.0))), floatingActionButton: FloatingActionButton( onPressed: () => logic.jump(), child: Icon(Icons.arrow_forward), ), ); }}///The binding layer
class TestOneBinding extends Bindings {
  @override
  voiddependencies() { Get.lazyPut(() => TestOneLogic()); }}Copy the code
  • TestTwo
///Logic layer
class TestTwoLogic extends GetxController {}///The view layer
class TestTwoPage extends StatelessWidget {
  final logic = Get.find<TestTwoLogic>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Page 2')),
      body: Center(child: Text('Page 2', style: TextStyle(fontSize: 30.0)))); }}///The binding layer
class TestTwoBinding extends Bindings {
  @override
  voiddependencies() { Get.lazyPut(() => TestTwoLogic()); }}Copy the code

conclusion

Here I have written a very simple example, just a function to jump to the page, I think, should be able to show the binding function

  • Manage multiple GetXControllers that need to be injected into a module
  • Note that this injection is lazy and will only be injected into getX’s global map instance if the find + generic is used

In fact, writing the binding file manually is still a bit cumbersome, and the view layer needs to be changed accordingly

In order to save you some development time, this waste of your life and no technical content has been done for you in the plugin

  • If necessary, select addBinding

  • GetPage binding operation, you have to do it yourself, the project structure is changing, it is impossible to locate this thing

Routing management

GetX implements a set of very simple route management, which can be navigated in a very simple way, or navigated using named routes

About the differences between simple and named routes

  • Simple routing: Very simple, see the example below
Get.to(SomePage());
Copy the code
  • After routing
    • On the Web, you can navigate a page directly from a named URL
    • Implementation of route interception operation, take an example of an official document: very easy to achieve a login, jump login page function
GetStorage box = GetStorage();

GetMaterialApp(
  getPages: [
    GetPage(name: '/', page:(){
      return box.hasData('token')? Home() : Login(); })])Copy the code

Simple routing

  • Main entrance configuration
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnGetMaterialApp( home: MainPage(), ); }}Copy the code
  • Use of routing
    • It is very simple to use, using an API like get.to (), which is briefly demonstrated here and described in detail at the end of this section
// Jump to a new page
Get.to(SomePage());
Copy the code

Named route navigation

Here is the recommended way to navigate using named routes

  • Unified management of all pages
  • You might not feel it in your app, but on the Web side, the URL to load the page is the named route string that you set, which means that on the Web, you can navigate directly to the relevant page through the URL

How to use it

  • First, under the main entry/exit configuration
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    returnGetMaterialApp( initialRoute: RouteConfig.main, getPages: RouteConfig.getPages, ); }}Copy the code
  • RouteConfig class
    • The following is my related page, and its mapping page, please write according to their own page
class RouteConfig {
  ///The main page
  static final String main = "/";

  ///Demonstrates himalaya Dialog page for SmartDialog control
  static final String smartDialog = "/smartDialog";
  static final String himalaya = "/himalaya";
  static final String dialog = "/dialog";

  ///The Bloc counter module Bloc delivers events across pages
  static final String blCubitCounterPage = "/blCubitCounterPage";
  static final String blBlocCounterPage = "/blBlocCounterPage";
  static final String cubitSpanOne = "/cubitSpanOne";
  static final String cubitSpanTwo = "/cubitSpanOne/cubitSpanTwo";
  static final String streamPage = "/streamPage";
  static final String blCustomBuilderPage = "/blCustomBuilderPage";
  static final String counterEasyCPage = "/counterEasyCPage";

  ///Test layout page
  static final String testLayout = "/testLayout";

  ///GetX counters interact across pages
  static final String getCounterRx = "/getCounterRx";
  static final String getCounterEasy = "/counterEasyGet";
  static final String getCounterHigh = "/counterHighGet";
  static final String getJumpOne = "/jumpOne";
  static final String getJumpTwo = "/jumpOne/jumpTwo";
  static final String getCounterBinding = "/getCounterBinding";

  ///Provider
  static final String proEasyCounterPage = "/proEasyCounterPage";
  static final String proHighCounterPage = "/proHighCounterPage";
  static final String proSpanOnePage = "/proSpanOnePage";
  static final String proSpanTwoPage = "/proSpanOnePage/proSpanTwoPage";
  static final String testNotifierPage = "/testNotifierPage";
  static final String customBuilderPage = "/customBuilderPage";
  static final String counterEasyPPage = "/counterEasyPPage";
  static final String counterGlobalEasyPPage = "/counterGlobalEasyPPage";

  ///Alias map page
  static final List<GetPage> getPages = [
    GetPage(name: main, page: () => MainPage()),
    GetPage(name: dialog, page: () => DialogPage()),
    GetPage(name: blCubitCounterPage, page: () => BlCubitCounterPage()),
    GetPage(name: blBlocCounterPage, page: () => BlBlocCounterPage()),
    GetPage(name: streamPage, page: () => StreamPage()),
    GetPage(name: blCustomBuilderPage, page: () => BlCustomBuilderPage()),
    GetPage(name: counterEasyCPage, page: () => CounterEasyCPage()),
    GetPage(name: testLayout, page: () => TestLayoutPage()),
    GetPage(name: smartDialog, page: () => SmartDialogPage()),
    GetPage(name: cubitSpanOne, page: () => CubitSpanOnePage()),
    GetPage(name: cubitSpanTwo, page: () => CubitSpanTwoPage()),
    GetPage(name: getCounterRx, page: () => GetCounterRxPage()),
    GetPage(name: getCounterEasy, page: () => GetCounterEasyPage()),
    GetPage(name: getCounterHigh, page: () => GetCounterHighPage()),
    GetPage(name: getJumpOne, page: () => GetJumpOnePage()),
    GetPage(name: getJumpTwo, page: () => GetJumpTwoPage()),
    GetPage(
      name: getCounterBinding,
      page: () => GetCounterBindingPage(),
      binding: GetCounterBinding(),
    ),
    GetPage(name: himalaya, page: () => HimalayaPage()),
    GetPage(name: proEasyCounterPage, page: () => ProEasyCounterPage()),
    GetPage(name: proHighCounterPage, page: () => ProHighCounterPage()),
    GetPage(name: proSpanOnePage, page: () => ProSpanOnePage()),
    GetPage(name: proSpanTwoPage, page: () => ProSpanTwoPage()),
    GetPage(name: testNotifierPage, page: () => TestNotifierPage()),
    GetPage(name: customBuilderPage, page: () => CustomBuilderPage()),
    GetPage(name: counterEasyPPage, page: () => CounterEasyPPage()),
    GetPage(name: counterGlobalEasyPPage, page: () => CounterGlobalEasyPPage()),
  ];
}
Copy the code

Routing API

Note the named route, just at the end of the APINamedFor example:

  • Default: Get to (SomePage ());
  • Named route: get.tonamed (” /somePage “);

For details on the Api, the following is a README document from GetX

  • Navigate to a new page
Get.to(NextScreen());
Get.toNamed("/NextScreen");
Copy the code
  • Close SnackBars, Dialogs, BottomSheets, or anything else you would normally close with navigator.pop (context)
Get.back();
Copy the code
  • Go to the next page without the option to return to the previous one (for SplashScreens, login pages, etc.)
Get.off(NextScreen());
Get.offNamed("/NextScreen");
Copy the code
  • Go to the next screen and cancel all previous routes (useful in shopping carts, polls, and tests)
Get.offAll(NextScreen());
Get.offAllNamed("/NextScreen");
Copy the code
  • Send data to other pages

Just send the parameters you want. Get accepts anything here, whether it’s a string, a Map, a List, or even an instance of a class.

Get.to(NextScreen(), arguments: 'Get is the best');
Get.toNamed("/NextScreen", arguments: 'Get is the best');
Copy the code

On your class or controller:

print(Get.arguments);
//print out: Get is the best
Copy the code
  • Navigate to the next route and receive or update the data as soon as you return
var data = await Get.to(Payment());
var data = await Get.toNamed("/payment");
Copy the code
  • On another page, send the data for the previous route
Get.back(result: 'success');
// And use it, for example:
if(data == 'success') madeAnything();
Copy the code
  • If you don’t want to use GetX syntax, just change Navigator (uppercase) to Navigator (lowercase) and you can have all the functionality of standard navigation without using context, for example:
// The default Flutter navigation
Navigator.of(context).push(
  context,
  MaterialPageRoute(
    builder: (BuildContext context) {
      returnHomePage(); },),);// Use the Flutter syntax, without the context.
navigator.push(
  MaterialPageRoute(
    builder: (_) {
      returnHomePage(); },),);/ / get the syntax
Get.to(HomePage());
Copy the code

Dynamic Web links

  • This is a very important feature that can be done on the Web sideEnsure that parameters are passed to the page through the URLIn the

Get provides advanced dynamic urls, just like on the Web. Web developers may already want this feature on Flutter, and Get solves this problem.

Get.offAllNamed("/NextScreen? device=phone&id=354&name=Enzo");
Copy the code

In your controller/bloc/stateful/stateless class:

print(Get.parameters['id']);
// out: 354
print(Get.parameters['name']);
// out: Enzo
Copy the code

You can also easily receive NamedParameters with Get.

void main() {
  runApp(
    GetMaterialApp(
      initialRoute: '/',
      getPages: [
      GetPage(
        name: '/',
        page: () => MyHomePage(),
      ),
      GetPage(
        name: '/profile/',
        page: () => MyProfile(),
      ),
       // You can define a different page for routes with parameters, or a different page for routes with no parameters, but you must use a slash "/" on routes that do not receive parameters, as stated above.
       GetPage(
        name: '/profile/:user',
        page: () => UserProfile(),
      ),
      GetPage(
        name: '/third',
        page: () => Third(),
        transition: Transition.cupertino  
      ),
     ],
    )
  );
}
Copy the code

Sends named route data

Get.toNamed("/profile/34954");
Copy the code

On the second page, you get the data by parameters

print(Get.parameters['user']);
// out: 34954
Copy the code

Now, all you need to do is use get.tonamed () to navigate your named route, without any context(you can call your route directly from your BLoC or Controller class), and your route will appear in the URL when your application is compiled to the Web.

Release resources

For GetxController resource release, this column is very important!

Scenario where resources are not released

When we use GetX, it probably doesn’t feel like GetxController is being released. This is because we usually use GetX’s jump API (Get. To, Get. ToName… To use get. toName, you must use GetPage; If you use get. to, you don’t need to register with GetPage. There is an operation added to GetPageRoute inside of get. to

It can be seen from the above that it will register with GetPage, indicating that when we jump to the page, GetX will store the information you sent to the page and manage it. The following two scenarios will cause GetxController to be unable to release

  • Route jumps not provided by GetX: Jump operations that use the native route API directly
    • This will directly cause GetX to be unable to perceive the life cycle of the corresponding page GetxController, which will cause it to be unable to be released
    • Register the page in GetPage without using get.toname, which cannot be released; GetPage+ get. toName can be released when used together
    • Use Get. To directly to release
Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => XxxxPage()),
);
Copy the code
  • Generally, apps have a home page with several tabs: home page, information flow page, operation page, personal center and so on
    • If you encounter the need to log in again and go back to the home page, you may find that the GetxController control for these pages in the personal center has not been reclaimed!
    • These pages are no longer related to the routing page, because they are only TAB pages on the main page and cannot be used to demarcate the binding relationship

The solution

Here I simulate the above scenario and write a solution

  • The first page jumps
Navigator.push(
    Get.context,
    MaterialPageRoute(builder: (context) => AutoDisposePage()),
);
Copy the code
  • The demo page
    • This is where the StatefulWidget must be used, because in this case, the StatefulWidget lifecycle is not aware
    • 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('Counter - Automatic release')),
      body: Center(
        child: Obx(
          () => Text('click on the${logic.count.value}Time ',
              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;

  ///Since the increase
  void increase() => ++count;
}
Copy the code

And you might look at this and think, ah! Why would I bother writing a StatefulWidget? Why would I bother writing a StatefulWidget?

Rest assured, this problem, I also thought of, I specially in the plug-in inside the automatic recycling function

  • If you write pages that can’t be recycled, check autoDispose
    • How do I determine if the page’s GetxController can be reclaimed? It’s actually quite simple, the unreleased scenario above has been described more clearly, if not, look again

Take a look at the code, default mode is also ok

  • 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(); }}Copy the code
  • logic
class AutoDisposeLogic extends GetxController {}Copy the code

Summary of some questions

If there are some problems, I hope you can raise them in the comments. I will summarize them in this column

  1. Use Get. To (Get. ToName) to jump to the page on the system Dialog without closing the Dialog; Return, jump again, there will be unable to jump

Debug the inside of the to method, jump on the system Dialog, and then jump back again. Internal routes do not match and return, and the hop fails

His internal API provides a solution

  • To jump to a page on the system Dialog, write
Get.to(XxxxPage(), preventDuplicates: false);
/ / or
Get.toNamed('xxx',  preventDuplicates: false);
Copy the code
  1. When using PageView, all PageView page controllers are initialized

If you use PageView, add PageView page, PageView page with GetX, you will find that all PageView page controllers are initialized! Instead of switching to a page, the corresponding controller is initialized!

When PageView switches to a Page, it calls the build method of the corresponding Page. For PageView, the controller injection process cannot be written in the class and needs to be initialized in the Build method.

  • Normal page, injection writing (non-PageView page)
class CounterEasyGetPage extends StatelessWidget {
  final CounterEasyGetLogic logic = Get.put(CounterEasyGetLogic());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter - Simple')),
      body: Center(
        child: GetBuilder<CounterEasyGetLogic>(
          builder: (logicGet) => Text(
            'click on the${logicGet.count}Time ',
            style: TextStyle(fontSize: 30.0),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: constIcon(Icons.add), ), ); }}Copy the code
  • PageView page, the initialization position must be adjusted
class CounterEasyGetPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CounterEasyGetLogic logic = Get.put(CounterEasyGetLogic());
  
    return Scaffold(
      appBar: AppBar(title: const Text('Counter - Simple')),
      body: Center(
        child: GetBuilder<CounterEasyGetLogic>(
          builder: (logicGet) => Text(
            'click on the${logicGet.count}Time ',
            style: TextStyle(fontSize: 30.0),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: constIcon(Icons.add), ), ); }}Copy the code

The last

The relevant address

  • DEMO address: flutter_use
  • GetX plugin address: getx_template
  • Windows:Windows platform installation package
    • Password: xdd666

series

Drainage, manual funny.png

  • GetX principle analysis: Flutter GetX depth profiling | we will out of my way (word graphic)

  • Farewell to Cthulhu Code Hill: Flutter improves nesting doll Hell problem

  • Make Dialog more possible: A more elegant Solution to the Flutter Dialog.