GetX profile

GetX is a lightweight and powerful solution for Flutter that combines high performance state management, intelligent dependency injection and fast and practical route management.

Last updated at: April 18

First, responsive controls

(a) Obx

Key words: StatelessWidget,.obs, Obx()

[Example: an add button — building responsive controls using.obs variables and Obx objects]

Here is an example of a simple view based on the observable variable and control Obx. Click a button to change the number on the page. This example shows that the GetX framework can implement state updates for controls built by the Build method without the page being 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), ), ); }}Copy the code

If you do not use the GetX framework (that is, the variable count is not suffixed. Obs or Text is not included in the control Obx), click the button + and the number on the screen does not increase; This is because the method build is executed only once and the onPressed change in the count value does not enter the control Text, so the number display on the screen does not change.

[Writing steps]

  • Create a Widget object using the StatelessWidget;
  • Declare an “observable variable “count, with the suffix.obs, that will be used in the control Obx;
  • Use the control Obx() to wrap the control Text() containing the variable count, usually in the format Obx(() => target status update control);

Note: Observable variables that use dynamic types cannot be used to construct parameters with type requirements;

GetX (2)

Key words:.obs, GetX()

[Example: Using someone else’s buttons (I) — View-logic separation based on observable variables]

GetX framework can load and use a controller other than the current class to update the state of the current view control, to achieve the separation of view and logic;

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

class Controller {
  var count = 0.obs;

  voidincrement() { 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(), ), ], ), ); }}Copy the code

Through the 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 change of count value caused by method increment cannot be passed to the control to achieve view state refresh. In addition to constructing the instance to create the controller, the implementation of this example also needs to declare the corresponding variable as “observable”.

[Writing steps]

1. Define observable variables in the controller;

  • Define the business logic class Controller;
  • The variable count will hold the data that needs to be updated, hence the suffix “.obs “;
  • The method increment can cause a change in the value of count.
  • The “observable” nature of the count variable means that when its value changes, the variable automatically responds and updates the value change;

2. Use the view control with observable variables as a child of the control GetX;

  • Create a view page: Use the StatelessWidget;
  • Build the response control: Pass the target control to update the status or display changes into the property Builder of the control GetX.

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

  • Create the action control: Set the update trigger in the onPressed property of FloatingActionButton
  • Each click executes the controller method increment;
  • When count changes, the state of the control built by Builder that displays the variable count will respond to the update;

Note: The control GetX is used only if the variable is declared observable, not if it inherits GetxController and uses get.put for injection.

GetxController & GetBuilder

Key words: GetxController, update, get.put (), GetBuilder()

[Example: Using someone else’s buttons (2) — Using GetBuilder to build view-logic detached responsive controls]

The difference between GetBuilder and GetX is that the latter can be observed based on variables, while the former updates the control state by loading the specified controller.

import 'package:get/get.dart';

class Controller extends GetxController {
  var count = 0;

  voidincrement() { count++; update(); }}Copy the code
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(), ), ], ), ); }}Copy the code

Through GetBuilder to achieve responsive control, the controller must inherit from GetxController, defined by the target state variable after no suffix “.obs “, but need to define the method update; And loading the specified controller not only requires injection using Get. Put, but GetBuilder also needs to bind the controller injected by specifying a generic binding.

[Writing steps]

  • Define the logic control class Controller, inherited fromGetxController;
    • Define the variable count to hold the state data
    • Define method increment to alter state data
    • In method increment, execute method update to update data state;
  • Use the StatelessWidget to create a view that targets the control that displays the change as the build object of GetBuilder;
    • Loading the Controlle instance with get-.put () makes the controller available to all child routes under the current Widget;
    • Pass the control that responds to the change into the property Builder of the GetBuilder generic specified as Controller;
    • Create the action control FloatingActionButton and set onPressed to execute the Controller method increment on each tap.
    • When the count changes, the control built by GetBuilder responds to the change and updates its state;

Get, put & find

Get. Put. Get

[Example: The most intuitive communication across component states — events are triggered on one page and state changes on another]

We’ve seen in the Get X framework that Get. Put can be used to inject controllers other than the current class. Use GetBuilder to build responsive control, not only need to Get. Put controller injection, but also require the controller as GetxController implementation class instance, and control GetBuilder must specify a generic to really Get the controller. Implement changes and updates to control state in the current view using business logic defined in another class;

But Get. Find makes these steps magically simpler: Use Get. Find to find and retrieve controllers that have been injected via Get. Put does not require the controller to come from the GetxController implementation class. There is no need to use controls like Obx, GetX, GetBuilder, etc. — the controller acquired through get.find can be applied directly to all child routes under the current Widget, enabling 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,),); }}Copy the code

The limitation of using Get. Find is that it must be applied to two or more widgets, and the view’s state response and refresh cannot be implemented in the same widget, because Get. Find can be used only if a controller has been injected in another widget via Get.

[Writing steps]

1. Define the business logic class Controller

  • Define variable count to hold status data (you can use “.obs “or update method)
  • Controller doesn’t have to inherit from GetxController

2, use Get. Put to inject controller, change count data through control

  • Create the interface using the StatelessWidget

  • Create Controller: construct Controller instance Controller in Get. Put () so that it is available to all child routes;

3. Use Get. Find to Get the controller and share the data state

  • Get. Find is called to find the injected controller from another Widget, the FirstPage page.

    Get, as a reference to the private object of the GetInterface subclass, calls find in the superclass extension, the same way a GetInstance calls find

  • Initialize the controller with the find method and save it as CTRL; Then share the data state in the count variable with CTRL;

4. Achieve cross-page state linkage: create a sliding page, click Button in FirstPage, and the value displayed in SecondPgae will change;

(5) Get to

[Example: Use Get. To to redirect routes]

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

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

// To use Get. To route pages, you need to build the 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) {
    // Trigger the jump with the RaisedButton onPressed event
    return RaisedButton(
      child: Text('Next Route'),
      onPressed: () {
        // Pass the target adjustment page as a parameter object.Get.to(TargetPage()); }); }}// Create the target jump page
class TargetPage extends StatelessWidget {
  @override
  Widget build(context) {
    return Scaffold(body: Center(child: Text("TargetPage"))); }}Copy the code

[Integration example: use Get. To + Get. Put + Get.

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}"))); }}Copy the code

Second, scalable applications

(a) Bindings

Bindings is the first step to having scalable applications;

Bindings is a class for managing dependency injection, which can take dependency injection out of widget tree, make view layer more purely used for placing widgets, and make code cleaner and more organized.

Injection into Bindings controller, allowing access anywhere without context;

Open the Bindings file on the page, you can clearly see the contents to be injected into the page;

import 'package:get/get.dart';

class HomeBinding extends Bindings {
  @override
  voiddependencies() { Get.lazyPut<Controller>(() => Controller()); }}Copy the code

Using a reference to Get as a private instance GetInstance, you can call the method lazyPut in the parent GetInterface extension

[Writing steps]

  • Create a class HomeBinding to inherit Bindings and override method dependencies (Bindings is an abstract class and method dependencies must be overwritten).
  • Use get.lazyput () in HomeBinding to lazily inject a Controller instance of the same type.
  • An instance of the Bindings class can be bound to a route, and the injected controller will be available to all child routes.

(2) the GetView

GetView, which is a StatelessWidget with a “Controller” property;

Use GetView to create interfaces in scalable applications;

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,
      ));
}
Copy the code

[Writing steps]

  • Create a page class Home that inherits from GetView, which identifies the source of the target controller by specifying a generic;
  • Import the controller type file. There is no need to construct the controller in the GetView page, so there is no need to Get.put() and Get.find().
  • Put the control construction with data status updates into **obx()**. When count changes, OBx will automatically accept the new Text();
  • Set the action control FloatingActionButton, onPressed to set each tap to call the Controller method increment;

(3) GetPage & GetMaterialApp

Set GetMaterialApp to the top of the Widget view;

The GetPage control can be used to construct a named routing page capable of setting specific dependency injection bindings;

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()),
    ],
  ));
}
Copy the code

[Writing steps]

  • Use GetMaterialApp instead of The MaterialApp (remember, avoid StatefulWidget whenever possible);
  • Construct the route control GetPage in the property getPages, named ‘/home’, which jumps to the page HOME;
  • Property initialRoute, which initializes the ‘/home’ route and binds its dependency injection as HomeBinding;

(iv) Basic steps for building scalable applications

1. Define the business logic class Controller

import 'package:get/get.dart';

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

  void increment() => count++;
}
Copy the code

2. Define Bindings class and inject dependencies

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

class HomeBinding implements Bindings {
  @override
  voiddependencies() { Get.lazyPut(() => HomeController()); }}Copy the code

3. Use GetView to create the interface

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,
      ));
}
Copy the code

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()),
    ],
  ));
}
Copy the code

Attached: a counter made with GetX

Official Document example

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': 'Ola DE podcast',},'pt_BR': {
      'title': 'Olá do Brasil',}}; }class Controller extends GetxController {
  int count = 0;
  void increment() {
    count++;
    // use update method to update all count variablesupdate(); }}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
  voiddependencies() { 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("The $_ has been changed (ever)"));

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

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

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

    /// Ignore all changes within 1 second.
    interval(count1, (_) => print("interval The $_ (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) {
    returnAlign( alignment: Alignment.center, child: SizeTransition( sizeFactor: CurvedAnimation( parent: animation, curve: curve, ), child: child, ), ); }}Copy the code

Official learning document: pub.flutter-io.cn/packages/ge…

Programming white, if there is fallacy, welcome to point out, heartfelt thanks!