Liberal arts students also program – simple use of geTx

Time:2021-11-30

Introduction to geTx

GeTx is a lightweight and powerful solution of flutter. It combines high-performance state management, intelligent dependency injection and fast and practical routing management.

Updated on: 4.18

1、 Responsive control

(1) OBX

Keywords: statelesswidget,. OBS, OBX ()

[example: an add button — use. OBS variable and OBX object to build a responsive control]

The following example is a simple view based on observable variables and control OBX. Click the button to change the number in the page;

This example shows that the geTx framework can update the state of the control built by the build method when the page is not refreshed by setstate().

import 'package:flutter/material.dart';
import 'package:get/get.dart';

// ignore: must_be_immutable
class GetXPage extends StatelessWidget {
  var count = 0.obs;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Obx(() => Text("$count"))
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count++,
        child: Icon(Icons.add),
      ),
    );
  }
}

If the geTx framework is not used (that is, the variable count has no suffix. OBS or text is not included in the control OBX), click the button +, and the number in the screen will not increase; This is because the method build is executed only once, and the change of count value caused by onpressed does not enter the text control, so the number display in the screen will not change.

[writing steps]

  • useStatelessWidgetCreate a widget object;
  • Declare an “observable variable” count with the suffix. OBS, which will be used in the control OBX;
  • useControl OBX ()The package contains the control text () of the variable count. The common format isOBX (() = > control of target update status)

Note: observable variables with dynamic types cannot be used for structural parameters with type requirements;

(2) GeTx

Keywords:. OBS, geTx ()

[example: using other people’s buttons (I) — view logic separation based on observable variables]

Using geTx framework, you can load and use the controller of non current class to update the state of current view control, so as to realize the separation of view and logic;

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class Controller {
  var count = 0.obs;

  void increment() {
    count++;
  }
}

class TestGetX extends StatelessWidget {
  final controller = Controller();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(170),
            child: GetX(builder: (_) => Text('clicks: ${controller.count}')),
          ),
          FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => controller.increment(),
          ),
        ],
      ),
    );
  }
}

Through the guide package itself, you can construct instances in other classes to access variables and methods in the class; However, if the geTx framework is not used, the value change of count caused by the method increment will not be transmitted to the control to refresh the view state; In addition to constructing an instance to create a controller, the implementation of the example also needs to declare the corresponding variables as “observable”.

[writing steps]

1. Define in controllerObservable variable

  • Define the business logic class controller;
  • The variable count will hold the data that needs to be updated, so the suffix“.obs “;
  • The method increment can cause the value of the variable count to change;
  • The “observable” feature of the variable count means that when its value changes, the variable can automatically respond to and update the value change;

2. Take the view control containing observable variables as the child control of geTx control;

  • Create view page: use statelesswidget;
  • Build response control: pass the control of target update status or display change into the property of geTx controlbuilderMedium;

3. Use the controller to update the value of the variable and observe the state response and change of the view control;

  • Create action control: set the update trigger in the onpressed property of floatingactionbutton
  • Each click will execute the controller’s method increment;
  • When count changes, the control state of the display variable count built by the builder will be updated in response;

Note: using the geTx controlpremiseYes, declare variablesObservable, instead of defining inheriting getxcontroller and injecting with get.put;

(3) Getxcontroller & getbuilder

Keywords: getxcontroller, update, get. Put(), getbuilder < s > ()

[example: using other people’s buttons (2) — building views using getbuilder – logic separated responsive controls]

useGetBuilderThe difference between implementing the response control and using geTx is that the latter is based on variable observability, while the former updates the control state by loading the specified controller;

import 'package:get/get.dart';

class Controller extends GetxController {
  var count = 0;

  void increment() {
    count++;
    update();
  }
}
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'test_controller.dart';

class TestGetBuilder extends StatelessWidget {
  final controller = Get.put(Controller());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(170.0),
            child: GetBuilder<Controller>(
                builder: (_) => Text(
                      'clicks: ${controller.count}',
                    )),
          ),
          FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () => controller.increment(),
          ),
        ],
      ),
    );
  }
}

The responsive control is implemented through getbuilder. The controller must inherit from getxcontroller, and there is no suffix after the defined target state variable“.obs“, but a method needs to be definedupdate; And load the specified controller, not only need to useGet.putFor injection, and getbuilder also needs to specifygeneric paradigmBind target injected controller.

[writing steps]

  • Define logic control class controller, inherited fromGetxController
    • Define the variable count to save the status data
    • Define the method increment to change the status data
    • In the method increment, execute the methodupdate, update data status;
  • Use statelesswidget to create a view, and take the control whose target display changes as the building object of getbuilder;
    • adoptGet.put() loadControl instance, the controller will be available to all sub routes under the current widget;
    • Pass in the control that responds to the change,Generic assignmentProperty of getbuilder for controllerbuilderMedium;
    • Create an action control floatingactionbutton, and set the method increment that will execute the controller each time you click in onpressed;
    • When the count changes, the control built by getbuilder will respond to the change and update the status;

(4) Get put & find

Keywords: get.put, get.find

[example: the most intuitive cross component state communication — trigger an event on this page and change the state on another page]

In the example of using someone else’s button (2), we have learned that get.put in get x framework can be used to inject non current class controllers; Building a responsive control with getbuilder requires not only controller injection through get.put, but also that the controller must be an instance of the getxcontroller implementation class, and the control getbuilder must specify a generic type to really get the controller, so as to change and update the control state in the current view using the business logic defined in another class;

However, get.find makes these steps magically Simplified: using get.find can find and obtain the controller injected through get.put. It does not require the controller to come from the implementation class of getxcontroller, nor does it need to use OBX, geTx, getbuilder and other controls – the controller obtained through get.find can be directly applied to all sub routes under the current widget, Achieve true cross component state communication.

import 'package:flutter/material.dart';
import 'package:get/get.dart';

class Controller {
  var count = 0;
}

class FirstPage extends StatelessWidget {
  final controller = Get.put(Controller());

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('Next Route'),
      onPressed: () {
        controller.count++;
      },
    );
  }
}

class SecondPage extends StatelessWidget {
  final Controller ctrl = Get.find();

  @override
  Widget build(context) {
    return Scaffold(body: Center(child: Text("${ctrl.count}")));
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(context) => Scaffold(
        body: SlidePage(),
      );
}

// ignore: must_be_immutable
class SlidePage extends StatelessWidget {
  List<Widget> pages = <Widget>[FirstPage(), SecondPage()];

  @override
  Widget build(BuildContext context) {
    return PageView.custom(
      scrollDirection: Axis.vertical,
      childrenDelegate: SliverChildBuilderDelegate(
        (BuildContext context, int index) {
          return pages[index];
        },
        childCount: 2,
      ),
    );
  }
}

The limitation of using get.find is that it must be applied to two or more widgets. If the state response and refresh of the view cannot be realized in the same widget, this is because the premise of using get.find is to obtain the controller that has been injected into another widget through get.put.

[writing steps]

1. Define business logic class controller

  • Define the variable count to save the status data (you can not use “. OBS” or update method)
  • The controller does not have to inherit from getxcontroller

2. UseGet.putInject the controller and change the count data through control

  • Creating an interface using statelesswidget

  • Create controller: construct controller instance in get. Put()controller, making the latter available to all current sub routes;

3. UseGet.findGet the controller and share the data status in it

  • Call get.find to find the injected controller from another widget, that is, the page firstpage;

    As a reference to the private object of the getinterface subclass, get calls find in the parent class extension, which is equivalent to a getInstance calling find

  • Initialize the controller through the find method and save it as Ctrl; Then share the data status in the variable count through Ctrl;

4. Realize cross page state linkage: create a sliding page, click the button in the page firstpage, and the value displayed in the page secondpgae will change;

(5) Get.to

[example: use get.to to realize route jump]

import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

//To use get.to for page routing, you need to build a control tree under getmaterialapp
class GetMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: GetPage(),
      // SlidePage(),
    );
  }
}

//Create a get routable page
class GetPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //The jump is triggered by the raisedbutton onpressed event
    return RaisedButton(
      child: Text('Next Route'),
      onPressed: () {
        //Transfer the target adjustment page as a parameter object to get.to;
        Get.to(TargetPage());
      },
    );
  }
}

//Create target jump page
class TargetPage extends StatelessWidget {
  @override
  Widget build(context) {
    return Scaffold(body: Center(child: Text("TargetPage")));
  }
}

[integration example: use get.to + get.put + get.find to realize page Jump count]

import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

  // testPrint();
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: GetPageTwo(),
      // SlidePage(),
    );
  }
}

class Controller extends GetxController {
  var count = 0;
}

class GetPageTwo extends StatelessWidget {
  final controller = Get.put(Controller());

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      child: Text('Next Route'),
      onPressed: () {
        controller.count++;
        Get.to(Second());
      },
    );
  }
}

class Second extends StatelessWidget {
  final Controller ctrl = Get.find();

  @override
  Widget build(context) {
    return Scaffold(body: Center(child: Text("${ctrl.count}")));
  }
}

2、 Scalable applications

(1) Bindings

Bindings is the first step to having a scalable application;

Bindings is a class that manages dependency injection. It pulls dependency injection out of the widget tree, so that the view layer is more purely used to arrange widget controls, making the code cleaner and more organized;

Injected into the controller of bindings, allowing any place without context to be accessed;

Open the bindings file on the page to clearly see the content to be injected into the page;

import 'package:get/get.dart';

class HomeBinding extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut<Controller>(() => Controller());
  }
}

Use the reference get as the private instance getInstance to call the method lazyput in its parent getinterface extension

[writing steps]

  • Create a homebinding class,Inherit bindings, and override method dependencies (bindings is an abstract class, and method dependencies must be overridden);
  • Use get. Lazyput() in homebinding to lazily inject a controller instance. Note that the types are consistent;
  • The instance of bindings class can be bound to the route. After binding, the injected controller will be available to all sub routes;

(2) Getview

Getview is a statelesswidget with “controller” attribute;

Use getview to create interface in scalable application;

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'home_controller.dart';

class Home extends GetView<HomeController> {
  @override
  Widget build(context) => Scaffold(
      appBar: AppBar(title: Text("counter")),
      body: Center(
        child: Obx(() => Text("${controller.count}")),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: controller.increment,
      ));
}

[writing steps]

  • Create page class home, inherited fromGetView, the latter passedSpecify genericsTo determine the source of the target controller;
  • To import a controller type file, you do not need to construct a controller in the getview page, so you do not need get. Put() and get. Find();
  • Put the control structure with data status update intoobx()When the count changes, OBX will automatically respond and receive a new text();
  • Set the operation control floatingactionbutton. In onpressed, the controller’s method increment will be called once for each click;

(3) Getpage & getmaterialapp

Set getmaterialapp to the top of the widget view;

Control getpage can be used to construct a named routing page that can set specific dependency injection binding;

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../home_view.dart';
import '../home_bindings.dart';

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/home',
    getPages: [
      GetPage(name: '/home', page: () => Home(), binding: HomeBinding()),
    ],
  ));
}

[writing steps]

  • useGetMaterialAppInstead of materialapp (remember, avoid statefulwidgets as much as possible);
  • Construct a routing control in the property getpagesGetPage, named ‘/ home’, it will jump to the page home;
  • attributeinitialRoute, initialize the ‘/ home’ route and inject its dependencies into homebinding;

(4) Basic steps for building scalable applications

1. Define business logic class controller

import 'package:get/get.dart';

class HomeController extends GetxController {
  var count = 0.obs;

  void increment() => count++;
}

2. Define the bindings class and inject dependencies

import 'package:get/get.dart';
import 'home_controller.dart';

class HomeBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HomeController());
  }
}

3. Create interface using getview

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'home_controller.dart';

class Home extends GetView<HomeController> {
  @override
  Widget build(context) => Scaffold(
      appBar: AppBar(title: Text("counter")),
      body: Center(
        child: Obx(() => Text("${controller.count}")),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: controller.increment,
      ));
}

4. Set getmaterialapp to the top of the widget view;

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../home_view.dart';
import '../home_bindings.dart';

void main() {
  runApp(GetMaterialApp(
    initialRoute: '/home',
    getPages: [
      GetPage(name: '/home', page: () => Home(), binding: HomeBinding()),
    ],
  ));
}

Attachment: a counter made with geTx

Example of official documents

import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(GetMaterialApp(
    // It is not mandatory to use named routes, but dynamic urls are interesting.
    initialRoute: '/home',
    defaultTransition: Transition.native,
    translations: MyTranslations(),
    locale: Locale('pt', 'BR'),
    getPages: [
      //Simple GetPage
      GetPage(name: '/home', page: () => First()),
      // GetPage with custom transitions and bindings
      GetPage(
        name: '/second',
        page: () => Second(),
        customTransition: SizeTransitions(),
        binding: SampleBind(),
      ),
      // GetPage with default transitions
      GetPage(
        name: '/third',
        transition: Transition.cupertino,
        page: () => Third(),
      ),
    ],
  ));
}

class MyTranslations extends Translations {
  @override
  Map<String, Map<String, String>> get keys => {
    'en': {
      'title': 'Hello World %s',
    },
    'en_US': {
      'title': 'Hello World from US',
    },
    'pt': {
      'title': 'Olá de Portugal',
    },
    'pt_BR': {
      'title': 'Olá do Brasil',
    },
  };
}

class Controller extends GetxController {
  int count = 0;
  void increment() {
    count++;
    // use update method to update all count variables
    update();
  }
}

class First extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.add),
          onPressed: () {
            Get.snackbar("Hi", "I'm modern snackbar");
          },
        ),
        title: Text("title".trArgs(['John'])),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GetBuilder<Controller>(
                init: Controller(),
                // You can initialize your controller here the first time. Don't use init in your other GetBuilders of same controller
                builder: (_) => Text(
                  'clicks: ${_.count}',
                )),
            RaisedButton(
              child: Text('Next Route'),
              onPressed: () {
                Get.toNamed('/second');
              },
            ),
            RaisedButton(
              child: Text('Change locale to English'),
              onPressed: () {
                Get.updateLocale(Locale('en', 'UK'));
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () {
            Get.find<Controller>().increment();
          }),
    );
  }
}

class Second extends GetView<ControllerX> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('second Route'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            GetX<ControllerX>(
              // Using bindings you don't need of init: method
              // Using Getx you can take controller instance of "builder: (_)"
              builder: (_) {
                print("count1 rebuild");
                return Text('${_.count1}');
              },
            ),
            GetX<ControllerX>(
              builder: (_) {
                print("count2 rebuild");
                return Text('${controller.count2}');
              },
            ),
            GetX<ControllerX>(builder: (_) {
              print("sum rebuild");
              return Text('${_.sum}');
            }),
            GetX<ControllerX>(
              builder: (_) => Text('Name: ${controller.user.value.name}'),
            ),
            GetX<ControllerX>(
              builder: (_) => Text('Age: ${_.user.value.age}'),
            ),
            RaisedButton(
              child: Text("Go to last page"),
              onPressed: () {
                Get.toNamed('/third', arguments: 'arguments of second');
              },
            ),
            RaisedButton(
              child: Text("Back page and open snackbar"),
              onPressed: () {
                Get.back();
                Get.snackbar(
                  'User 123',
                  'Successfully created',
                );
              },
            ),
            RaisedButton(
              child: Text("Increment"),
              onPressed: () {
                Get.find<ControllerX>().increment();
              },
            ),
            RaisedButton(
              child: Text("Increment"),
              onPressed: () {
                Get.find<ControllerX>().increment2();
              },
            ),
            RaisedButton(
              child: Text("Update name"),
              onPressed: () {
                Get.find<ControllerX>().updateUser();
              },
            ),
            RaisedButton(
              child: Text("Dispose worker"),
              onPressed: () {
                Get.find<ControllerX>().disposeWorker();
              },
            ),
          ],
        ),
      ),
    );
  }
}

class Third extends GetView<ControllerX> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(onPressed: () {
        controller.incrementList();
      }),
      appBar: AppBar(
        title: Text("Third ${Get.arguments}"),
      ),
      body: Center(
          child: Obx(() => ListView.builder(
              itemCount: controller.list.length,
              itemBuilder: (context, index) {
                return Text("${controller.list[index]}");
              }))),
    );
  }
}

class SampleBind extends Bindings {
  @override
  void dependencies() {
    Get.lazyPut<ControllerX>(() => ControllerX());
  }
}

class User {
  User({this.name = 'Name', this.age = 0});
  String name;
  int age;
}

class ControllerX extends GetxController {
  final count1 = 0.obs;
  final count2 = 0.obs;
  final list = [56].obs;
  final user = User().obs;

  updateUser() {
    user.update((value) {
      value.name = 'Jose';
      value.age = 30;
    });
  }

  /// Once the controller has entered memory, onInit will be called.
  /// It is preferable to use onInit instead of class constructors or initState method.
  /// Use onInit to trigger initial events like API searches, listeners registration
  /// or Workers registration.
  /// Workers are event handlers, they do not modify the final result,
  /// but it allows you to listen to an event and trigger customized actions.
  /// Here is an outline of how you can use them:

  /// made this if you need cancel you worker
  Worker _ever;

  @override
  onInit() {
    /// Called every time the variable $_ is changed
    _ever = ever(count1, (_) => print("$_ has been changed (ever)"));

    everAll([count1, count2], (_) => print("$_ has been changed (everAll)"));

    /// Called first time the variable $_ is changed
    once(count1, (_) => print("$_ was changed once (once)"));

    /// Anti DDos - Called every time the user stops typing for 1 second, for example.
    debounce(count1, (_) => print("debouce$_ (debounce)"),
        time: Duration(seconds: 1));

    /// Ignore all changes within 1 second.
    interval(count1, (_) => print("interval $_ (interval)"),
        time: Duration(seconds: 1));
  }

  int get sum => count1.value + count2.value;

  increment() => count1.value++;

  increment2() => count2.value++;

  disposeWorker() {
    _ever.dispose();
    // or _ever();
  }

  incrementList() => list.add(75);
}

class SizeTransitions extends CustomTransition {
  @override
  Widget buildTransition(
      BuildContext context,
      Curve curve,
      Alignment alignment,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child) {
    return Align(
      alignment: Alignment.center,
      child: SizeTransition(
        sizeFactor: CurvedAnimation(
          parent: animation,
          curve: curve,
        ),
        child: child,
      ),
    );
  }
}

Official learning documents:https://pub.flutter-io.cn/packages/get#about-get

Programming Xiaobai, if there are fallacies, welcome to point out, thank you sincerely!

Recommended Today

Game case | application evolution and practice of service mesh in happy games

author Chen Zhiwei, Tencent level 12 background expert engineer, is now responsible for the public background technology research and development and team management of happy game studio. Rich experience in micro service distributed architecture and game background operation and maintenance research and development. preface The background of happy game studio is a distributed micro service […]