The use of fluent geTx --- simple charm!, In depth explanation by Ali technical experts

  *   Direct use Get.to,Releasable
Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => XxxxPage()),
); 
  • Generally, an app is a home page with several tab s: home page, information flow page, operation page, personal center, etc
    • If you need to log in again and return to the home page, you may find that the GetxController control of these pages in the personal center has not been recycled!
    • These pages have nothing to do with the above routing pages, because they can only be regarded as several tab sub pages on the main page, and there is no way to calibrate the binding relationship with routing

Solution

Here, I simulated the above scenario and wrote a solution

  • First page Jump
Navigator.push(
    Get.context,
    MaterialPageRoute(builder: (context) => AutoDisposePage()),
); 
  • Demo page
    • StatefulWidget must be used in this place, because in this case, the lifecycle cannot be perceived, so StatefulWidget lifecycle 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('Counter-Automatic release')),
      body: Center(
        child: Obx(
          () => Text('Click ${logic.count.value} second',
              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;

  ///Self increasing
  void increase() => ++count;
} 

Seeing this, you might think, ah! Why is it so troublesome? Why should I write StatefulWidget? It's so troublesome!

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

  • If the page you write cannot be recycled, remember to check autoDispose
    • How to determine whether the GetxController of a page can be recycled? In fact, it's very simple. The above scenes that have not been released have been described clearly. If not, let's take a look

Take a look at the code. The default mode is 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();
  }
} 
  • logic
class AutoDisposeLogic extends GetxController {

} 

OK, next, enter the text!

Counter

design sketch

  • [experience](

)

realization

The home page, of course, is to implement a simple counter. Let's see how GetX decouples the logic layer from the interface layer

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

Let's take a look at the generated default code. The default code is very simple. The detailed explanation is put in two state management

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

class CounterGetLogic extends GetxController {

} 
  • 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) {
    return Container();
  }
} 

Responsive state management

When the data source changes, the method of refreshing the component is automatically executed

  • logic layer
    • Because it deals with page logic and the Controller word is too long, it also prevents confusion with some control controllers provided by Flutter. Therefore, this layer ends with logic. Here, it is defined as the logic layer. Of course, this can be written as Event and Controller according to personal intention
    • Write. obs after the variable value here to explain that the variable is defined as a responsive variable. When the variable value changes, the page refresh method will refresh automatically; the basic type, List and class can add. obs to make it a responsive variable
class CounterGetLogic extends GetxController {
  var count = 0.obs;

  ///Self increasing
  void increase() => ++count;
} 
  • view layer

    • After obtaining the instance of the Logic layer, you can operate it. You may think: WTF, why is the instance operation in the build method? Tease me? -------- actually, otherwise, stl is a stateless component, which shows that it will not be reorganized twice, so the instance operation will only be executed once, and Obx() The method can refresh the components, which perfectly solves the problem of refreshing the components

      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 ${logic.count.value} second',
                    style: TextStyle(fontSize: 30.0)),
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: () => logic.increase(),
              child: const Icon(Icons.add),
            ),
          );
        }
      } 
      
    • Of course, it can also be written like this

      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 ${logic.count.value} second',
                    style: TextStyle(fontSize: 30.0)),
              ),
            ),
            floatingActionButton: FloatingActionButton(
              onPressed: () => logic.increase(),
              child: const Icon(Icons.add),
            ),
          );
        }
      } 
      
    • It can be found that the method of refreshing components is extremely simple: Obx(), so that you can write fixed-point refresh operations everywhere happily

  • Conditions for Obx() method refresh

    • The refresh operation will be performed only when the value of a responsive variable changes. When the initial value of a variable is "test" and then assigned to "test", the refresh operation will not be performed
    • When you define a responsive variable, the Obx() method wrapping the responsive variable will perform the refresh operation. Other Obx() methods not wrapping the responsive variable will not perform the refresh operation, Cool!
  • Let's see how to implement the update operation if the entire class object is set to the response type?

    • The following explanation comes from the official README document
    • Here's an attempt to set the entire class object as the response type. When you change one of the variables of the class, and then perform the update operation, the refresh operation will be performed as long as Obx() wrapped with the variable of the response class. Set the entire class as the response type, which needs to be used in combination with the actual scene
// model
// We will make the entire class observable, not 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 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 it is the user variable, not the class variable (the first letter is lowercase). 

Simple state management

GetBuilder: This is an extremely lightweight state manager, occupying very few resources!

  • Logic: let's look at the logic layer first
class CounterEasyGetLogic extends GetxController {
  var count = 0;

  void increase() {
    ++count;
    update();
  }
} 
  • 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 formula')),
      body: Center(
        child: GetBuilder<CounterEasyGetLogic>(
          builder: (logicGet) => Text(
            'Click ${logicGet.count} second',
            style: TextStyle(fontSize: 30.0),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: const Icon(Icons.add),
      ),
    );
  }
} 
  • Analysis: GetBuilder method
    • Init: Although the above code is not used, this parameter exists in GetBuilder, because the CounterEasyGetLogic object is generated by using Get.put() when loading variables, and GetBuilder will automatically find the object. Therefore, the init parameter can not be used
    • builder: method parameter, with an input parameter. The type is the type of generic passed in by GetBuilder
    • initState, dispose, etc.: GetBuilder has all periodic callbacks of StatefulWidget, and can do some operations in the corresponding callbacks

summary

analysis

  • Obx is used in conjunction with Rx responsive variables and GetBuilder is used in conjunction with update: Please note that this is entirely a scheme of two sets of fixed-point refresh controls
    • Difference: the former responds to variable changes and Obx automatically refreshes; the latter needs to use update to manually call refresh
  • Because StreamBuilder is used, it will consume some resources
  • GetBuilder actually encapsulates StatefulWidget internally, so it takes very little resources

Usage scenario

  • In general, you can use responsive variables for most scenarios
  • However, if a List containing a large number of objects uses responsive variables, a large number of streambuilders will be generated, which will cause great pressure on memory. In this case, it is necessary to consider using simple state management

Cross page interaction

Cross page interaction is a very important function in complex scenarios. Let's see how GetX realizes cross page event interaction

design sketch

  • [experience](

)

  • Cool, this is the real cross page interaction! The subordinate page can call the superior page event at will, and after closing the page, the data will be reset naturally next time (the global Bloc will not be reset, but needs to be reset manually)

realization

Page one

General code

  • logic
    • The auto increment event here is called by other pages and is not used by the page itself
class JumpOneLogic extends GetxController {
  var count = 0.obs;

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

  ///Jump to cross page
  void increase() => count++;
} 
  • view
    • Here is a display text and jump function
class JumpOnePage extends StatelessWidget {
  ///Use Get.put() to instantiate your class and make it available to all current sub routes.
  final JumpOneLogic logic = Get.put(JumpOneLogic());

  @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: Obx(
          () => Text('Cross page-Two Click ${logic.count.value} second',
              style: TextStyle(fontSize: 30.0)),
        ),
      ),
    );
  }
} 

Page 2

This page is the point

  • logic
    • Demonstrates how to invoke the event of the previous page
    • How to receive data from the previous page
    • Please note that GetxController contains a relatively complete life cycle callback, which can accept the passed data in onInit(); If the received data needs to be refreshed to the interface, please receive data operations in the onReady callback, onReady is invoked in the addPostFrameCallback callback, and the operation of refreshing data is carried out in onReady, which ensures that the interface is refreshed after the initial load is completed.
class JumpTwoLogic extends GetxController {
  var count = 0.obs;
  var msg = ''.obs;

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

    super.onReady();
  }

  ///Jump to cross page
  void increase() => count++;
} 
  • view
    • The click event of plus sign, when clicked, can realize the transformation of two page data
    • Here's the point. You can get the GetXController instantiated before through Get.find(). After you get the GetXController of a module, you can call the corresponding event through this GetXController or get the data of the module through it!
class JumpTwoPage extends StatelessWidget {
  final JumpOneLogic oneLogic = Get.find();
  final JumpTwoLogic twoLogic = Get.put(JumpTwoLogic());

  @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
          Obx(
            () => Text('Cross page-Two Click ${twoLogic.count.value} second',
                style: TextStyle(fontSize: 30.0)),
          ),

          //Transfer data
          Obx(
            () => Text('Data transferred: ${twoLogic.msg.value}',
                style: TextStyle(fontSize: 30.0)),
          ),
        ]),
      ),
    );
  }
} 

summary

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

Advance! Counter

We may encounter many complex business scenarios. In complex business scenarios, there may be many initialization operations on variables in a module alone. At this time, if the state (state layer) and logic (logic layer) are also written together, it may look dizzy to maintain. Here, split the state layer and logic layer, In this way, using GetX in slightly larger projects can also ensure that the structure is clear enough!

Here continue to use the counter as an example!

realization

Here we need to divide into three structures: state (state layer), logic (logic layer) and view (interface layer)

  • Here, the plug-in is used to generate the template code
    • Model: select Default
    • Function: useFolder (default)

Take a look at the generated template code

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

import 'state.dart';

class CounterHighGetLogic extends GetxController {
  final state = CounterHighGetState();
} 
  • 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) {
    return Container();
  }
} 

Why do you need to put forward the State separately when writing such three modules? Please browse below quickly

reform

  • state
    • It can be found that the count type uses RxInt instead of var. the reason for using this variable type is that all operations are initialized in the constructor. If VaR is directly used without immediate assignment, it cannot be derived into Rx type. Therefore, RxInt is directly defined here. In fact, it is very simple. The basic type will capitalize the beginning letter, Then add Rx prefix
    • In fact, it is also possible to use var directly. However, when using this response variable. value cannot be prompted and needs to be written by yourself, so you'd better state the specific type of Rx honestly
    • Details can be viewed: [declare responsive variable](

)

class CounterHighGetState {
  RxInt count;

  CounterHighGetState() {
    count = 0.obs;
  }
} 
  • logic
    • The logic layer is relatively simple. It should be noted that the state class needs to be instantiated at the beginning
class CounterHighGetLogic extends GetxController {
  final state = CounterHighGetState();

  ///Self increasing
  void increase() => ++state.count;
} 
  • view
    • In fact, the view layer is almost the same as the previous one. The difference is that the state layer is independent
    • Because CounterHighGetLogic is instantiated, you can directly use get. Find < CounterHighGetLogic > () to get the logic layer just instantiated, then get the state, and use a separate variable to receive it
    • ok, at this time: logic only focuses on triggering event interaction, and state only focuses on data
class CounterHighGetPage extends StatelessWidget {
  final CounterHighGetLogic logic = Get.put(CounterHighGetLogic());
  final CounterHighGetState state = Get.find<CounterHighGetLogic>().state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter-Responsive')),
      body: Center(
        child: Obx(
              () => Text('Click ${state.count.value} second',
              style: TextStyle(fontSize: 30.0)),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.increase(),
        child: const Icon(Icons.add),
      ),
    );
  }
} 

contrast

Looking at the above transformation, you may want to make complaints about the screen before: pit than ah, before the simple logic layer was broken down into two, and it was so troublesome. Are you a monkey invited?

Don't make complaints about it. When business is too complicated, state layer will also maintain many things. Let's look at the following chestnut. The following example code can not run directly. Let's look at the detailed operation code. Please check the project: [flutter_use] (

)

last

All materials of the article have been packaged and sorted out. In addition, Xiaobian has sorted out a large number of full sets of learning materials for Android architects, PDF document of Android core advanced technology + full set of advanced learning materials + Video + real problem analysis of 2021 BAT factory interview

**[CodeChina open source project: Android learning notes summary + mobile architecture Video + big factory interview real questions + project actual combat source code](

)**

Data display:

This article has been Tencent CODING open source hosting project: Android learning notes summary + mobile architecture Video + big factory interview real questions + project actual combat source code Collection, self-study resources and series of articles are continuously updated
So troublesome, are you the monkey's Teaser?

Don't make complaints about it. When business is too complicated, state layer will also maintain many things. Let's look at the following chestnut. The following example code can not run directly. Let's look at the detailed operation code. Please check the project: [flutter_use] (

)

last

All materials of the article have been packaged and sorted out. In addition, Xiaobian has sorted out a large number of full sets of learning materials for Android architects, PDF document of Android core advanced technology + full set of advanced learning materials + Video + real problem analysis of 2021 BAT factory interview

**[CodeChina open source project: Android learning notes summary + mobile architecture Video + big factory interview real questions + project actual combat source code](

)**

Data display:

[external chain picture transferring... (img-v6ymma1l-1631249494194)]

[external chain picture transferring... (img-rbr6hxxb-1631249494195)]

[external chain picture transferring... (img-JGDw7gWA-1631249894196)]

[external chain picture transferring... (img-NyP0h2UL-1631249894197)]

This article has been Tencent CODING open source hosting project: Android learning notes summary + mobile architecture Video + big factory interview real questions + project actual combat source code Collection, self-study resources and series of articles are continuously updated

Tags: Design Pattern Programmer Flutter

Posted on Sat, 20 Nov 2021 07:51:32 -0500 by helraizer