GetX as an StateManagement: A Complete Guide

Flutter Guide - GetX as State management

GetX is one of the popular state management package among the flutter developer. GetX is not just famous for high performances state management but the combination of intelligent dependency injection and route management also. So, It is the most liked packages with the likes of 7.56k as of now. As there are a lot of state management tools in flutter, GetX tops the list by being fast, stable, extra-light and powerful solution for flutter framework.

Related Read: Flutter Bloc: A Complete Guide

Developers are always trying to manage their application state in simple and well organized fashion while improving performances. GetX helps developers realize a high level of productivity through easy and pleasant syntax without sacrificing app performance. And GetX package helps the flutter developer to achieve it with these features:

High Performances

GetX is focus on high performance along with the minimum consumption of resources. Since, it doesn’t use Streams or ChangeNotifier.

High Productivity

Using simple and easy syntax, GetX will save hours of development and provide high performance. The developer should be careful about removing controllers from memory in general. This is not necessary with GetX because resources are automatically removed from memory when they are not in use. We must explicitly declare “permanent: true” in our dependent if we want it to be kept in memory. That way, in addition to saving time, we are less at risk of having unnecessary dependencies on memory.

Simple Code Organization

GetX helps to maintain the clean architecture by total decoupling of the View, presentation logic, business logic, dependency injection, and navigation. We do not need context to navigate between routes, so we are not dependent on the widget tree (visualization) for this.

Context is not required

In an application, context is very important since it is a link to the location of a widget in the tree structure of widgets. And sending the context from view to controller is tedious. But in GetX, we can access controllers within another controller without any context.

No code generation

While using other state management tools, we have to use the code generator (build_runner) after a slightly change on our code which is tedious and time consuming. But with GetX we don’t have to use the code generator at all.

Less Code

With GetX we don’t have to write boilerplate code like bloc pattern and other state management tools. Since we don’t have to create classes and event like bloc, we can achieve the high performances and productivity with much faster and less code.

Installing

Add Get to your pubspec.yaml file:

dependencies:
  get:

Import get in files that it will be used:

import 'package:get/get.dart';

The generated code from the main.dart file by rendering Home is as follows:

import 'package:flutter/material.dart';
import 'package:getx_article/home.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Home(),
    );
  }
}

But to get started with the GetX, we have to replace the MaterialApp with the GetMaterialApp. This is because we can use GetX for Navigation. The GetMaterialApp is a class from the package.

Then our code with the GetX environment set up will be like:

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Home(),
    );
  }
}

Controllers

Before jumping into the GetX statemanagement, we must have knowledge about the controllers. Controllers are means to give control to the parent widget over its child state which contains all of your business logic for the application. This will make it easy to track different issues or errors. And GetX also contains the class called GetxController. We have to extend our controller from GetXController.

import 'package:get/get.dart';

class StateController extends GetxController{

  // your state variables
  //your methods
  
}

There are three popular methods which is also called lifecycle methods in the GetXController. They are onInit(), onClose() and onReady(). When your controller is created in memory, the onInit() method is called immediately, and the onClose() method is called when it is removed from memory. Soon after the widget has been rendered on the screen, the onReady() method will be invoked. Using this methods, we can replace the initState() and dispose() methods in StatefulWidget.

import 'package:get/get.dart';
class StateController extends GetxController{
  @override
  void onInit() {
    // Here you can initialize and fetch you product from server 
    super.onInit();
  }

  @override
  void onReady() {

 // Called 1 frame after onInit(). It is the perfect place to enter
  // navigation events, like snackbar, dialogs, or a new route, or
  // async request.

    super.onReady();
  }

  @override
  void onClose() {
    // Here, you can dispose your StreamControllers
    // you can cancel timers
    super.onClose();
  }

}

StateManagement with GetX

There are two types of state managers with GetX. They are Simple State manager (Non-Reactive) and Reactive State manager. The main difference between them is Simple state manager updates the UI only when we want to whereas Reactive State manager data depends on stream and when the data changes all the listeners will update immediately.

Simple State Manager (Non-Reactive)

Non-Reactive state manager is simple state management in GetX which updates the UI only when we want to. The main point of using Simple state manager is to avoid the Streams and ChangeNotifier with uses of extremely low resources. So in order to listen the changes of state, update() method is used. We must call the update function after making changes to our state in our controller to tell the widgets that are listening to the state.

Let’s us see the example of counter increasing and decreasing:

import 'package:get/get.dart';

class SimpleStateController extends GetxController {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;        //for increasing the counter.
    update();   //
for notifying the change in state.
  }

  void decrement() {
    _count--;        //for decreasing the counter.
    update();
  }
}

In the above code, update() function is called after each changes in the state.

For Simple state manager, GetBuilder is used to change the view based on the changing state.

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:getx_article/controller/state_controller.dart';

class Home extends StatelessWidget {
  Home({Key? key}) : super(key: key);
  final _controller = Get.put(SimpleStateController()); // dependency injection

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          FloatingActionButton(
            onPressed: () {
              _controller
                  .increment(); //calling increment function from controller
            },
            child: const Icon(Icons.add),
          ),
          FloatingActionButton(
            onPressed: () {
              _controller
                  .decrement(); //calling decrement function from controller
            },
            child: const Icon(Icons.remove),
          )
        ],
      ),
      appBar: AppBar(
        title: const Text("GetX as State Management"),
      ),
      body: GetBuilder<SimpleStateController>(
          //GetBuilder is used to change the view based on changing state in GetX

          init: _controller, //initializing the controller
          builder: (controller) => Center(
                child: Text(
                  "Count = ${_controller.count}",
                  //accessing the variable to change the UI from controller.
                  style: const TextStyle(
                      fontSize: 28, fontWeight: FontWeight.bold),
                ),
              )),
    );
  }
}

Reactive State Manager

Using the update() method to listen after every changes in state is tedious. But in reactive state manager, our data depends on Streams. When data changes, all listeners will be notified right away. In order to do that in reactive approach, we need to create our variables observable. It helps to our widgets to keep track of changes in our variables and update on UI immediately. One of the method to convert the variables into reactive variables or observable variables is using GetX or ObX. Both of them is used as reactive state manager but GetX has lifecycle methods like init() and dispose() but ObX doesn’t.

Let’s us see the example of counter increasing and decreasing:

import 'package:get/get.dart';

class ReactiveStateController extends GetxController {
  var _count = 0.obs;    //Making the variable observable 

  int get count => _count.value;   

  void increment() {
    _count++;      //increasing counter
  }

  void decrement() {
    _count--;     //decreasing counter
  }
}

In the above code, we make the variable reactive by adding .obs at the end of the variables. This way code became shorter as easy to read.

For Reactive state manager, Let us use the GetX to change the view based on the changing state. First replace the dependency injection with the ReactiveStateController as below.

 final _controller = Get.put(ReactiveStateController()); // dependency injection

Then replace the GetBuilder in the home with the following code:

GetX<ReactiveStateController>(              //GetX to change view based on changing state.
      init: ReactiveStateController(),      //initializing reactive state controller
         builder: (controller) => Center(
             child: Text(
                "Count = ${_controller.count}",  //accessing the variable to show changing count.
                 style: const TextStyle(
                   fontSize: 28, fontWeight: FontWeight.bold),
               ),
          )),
    );

ObX is lighter version of GetX. It is much more simple than GetX. We don’t have to initialize the Controller in ObX. we just have to wrap our widget from it.

Obx(() => Center(
        child: Text(
                "Count = ${_controller.count}",
                style: TextStyle(fontSize: 28, fontWeight: FontWeight.w500),
              ),
      )),

With the use of GetX package, we no longer need the StatefulWidgets in our application. we don’t need to waste our resources by making StatefulWidgets since, we can handle our UI state in simple and cleaner way using GetX or ObX than SetState.

If think otherwise about GetX? Read this article about Riverpod.