The Flutter GetX State Management                                       
Tags: flutter, tutorial, android, ios
Published: 2021-09-16 13:39:17

Flutter is without a doubt the best framework for developing cross-platform applications. Application development with Flutter is truly awesome and easy because it provides a rich set of customizable widgets. However, some state management options won't allow you to feel the true power of the flutter framework, since you have to waste your development time to implement unnecessary boilerplate. When I started to learn the Bloc pattern, I was confused by the bloc concepts. It was difficult to understand. On the other hand, the provider is easy to understand, but we have to be very careful when avoiding the unnecessary rebuilds. Since It directly affects your application's performance. However, all the state management options have their pros and cons.

GetX has a different philosophy. It wants to manage your application state in a simple and well organized fashion while improving performance. So let’s see how GetX has achieved it.

In the article, I will discuss,

1. Why is GetX so special?
2. State management using GetX
3. GetxController
4. The Reactive State Manager in GetX
5. The Simple State Manager in GetX
6. MixinBuilder : Mix your both state managers
7. StateMixin

## Why is GetX so special?

GetX is more than just a state management library. It is, in fact, a small flutter framework capable of handling route management and dependency injection in flutter applications. But in this article, I will only discuss its state management capabilities. 
GetX is a very lightweight and powerful state management solution for flutter. So why is GetX so superior?

**High performance:** GetX uses fewer resources as possible. It does not depend on Streams or ChangeNotifier. Instead, it uses low latency GetValue and GetStream to improve performance.

**Less code:** You may be tired of implementing boilerplate in the bloc pattern and waste development time on unnecessary codes. Time is money, right? In GetX, you are not going to write any boilerplate. You can achieve the same thing much faster, with less code in GetX. No need to create classes for the state and event, since these boilerplates do not exist in GetX.

**No code generation:** There is no need to use code generators at all. So your valuable development time is not going to waste any more on running code generators(build_runner) every single time when you change your code. cool right?

**Don't worry about context:** Your application context is very important. But sending the context from your view to the controller can be, sometimes cumbersome. In GetX, you don't need to do this. You can access controllers within another controller without any context. cool right?

**No unnecessary rebuilds:** Unwanted rebuilds are a problem of state managers based on ChangeNotifier. When you make a change in your ChangeNotifier class, all widgets that depend on that ChangeNotifier class are rebuilt. Some rebuilds may be unnecessary and costly.  It may also reduce the application's performance as well. You don't have to worry about this in GetX since it does not use the ChangeNotifier at all. 

**Code organization is simple:**  Bloc's popularity comes from its superior code organizing capabilities. It makes it easier to separate your business logic from the presentation layer. GetX is a natural evolution for this as official documentation says. In GetX, you can separate not just the business logic but also the presentation layer. Powerful right?

So, what do you think about GetX? Can I say superior for it? I think I can.

##State management using GetX

GetX provides two kinds of state managers: The reactive state manager and the simple state manager. If you have used Bloc before, then you should have some experience in reactive programming. In GetX, you can have far more superior and easier reactive experience, unlike Bloc. The simple state manager is just like using setState in StatefulWidget, but in a cleaner way. Before discussing these two state managers, it is essential to know about `GetxController` in GetX.

##GetxController

Your controllers contain all of your business logic. GetX has an important class called `GetxController`. It is useful to enable reactive and simple state manager functionality in your controllers. All you have to do is to extend your controllers from GetxController. 

Let's take a simple example from your shopping app. 
```javascript
class ProductController extends GetxController {
	// your state variables
	// your methods
}
```
You can completely remove StatefulWidget by using GetxController. Since GetxController has `onInit()` and `onClose()` methods. So you can replace `initState()` and `dispose()` methods in StatefulWidget. Pretty clever  right? 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.

You can also use the `onReady()` method in GetxController. The onReady() method will be called soon after the widget has been rendered on the screen.

```dart
class ProductController extends GetxController {

     @override 
	void onInit() {
	   // Here you can fetch you product from server
	   super.onInit();
	}

	@override 
	void onReady() {
	   super.onReady();
	}

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

Thanks to the `DisposableInterface`, GetxController can dispose of your controllers from memory on its own. So you don't need to dispose of anything  manually anymore. GetxContoller will take care of it for you.  As a result, it will help to reduce the memory consumption and improve the application performance. 

##The Reactive State Manager in GetX

The reactive state manager implements reactive programming in an easier and cleaner way. You may have used StreamContollers and StreamBuilder in your reactive programming approaches. But in GetX, you won't need to create such things. Furthermore, unlike Bloc, there is no need to create separate classes for each state. You can remove these boilerplates and do the same thing with just a few lines of code using Getx.

###Create Reactive Variables

In the Reactive approach of GetX, first you need to create observable variables(reactive variables). In simple terms, your widgets can watch changes of your variables. And widgets can update their UI according to these changes in variables. There are three different ways to create reactive variables.

  **1. Attaching Rx to variable type,** `Rx{Type}`
```dart
class CounterController extends GetxController {
	var counter = RxInt(0);  // You can add 0 as the initial value
} 
``` 
 **2. Using Rx and Dart Generics,** `Rx<Type>`
```dart
class CounterController extends GetxController {
	var counter = Rx<Int>(0);  // You can add 0 as the initial value
} 
```

 **3. Adding `.obs` to the end**
```dart
class CounterController extends GetxController {
	var counter = 0.obs; 
}
```
That's it. Simple right, You can use any approach you like.

###Using `GetX`  and `Obx`

You can use `GetX` or `Obx` to listen to changes of your reactive variables from your widgets. 
`GetX<Controller>` is just like StreamBuilder, but without a boilerplate.
`Obx` is much more simple than GetX. You just have to wrap your widget from it.
```dart
class CounterController extends GetxController {
	var counter = 0.obs; 
}  
```
Using GetX:
```dart
GetX<CounterController>(
    init: CounterController(),
    builder: (controller) => Text(
	     'Counter is ${controller.counter.value}'
	),
),
```
Using Obx:
```dart
Obx(() => Text(
	    'Counter is ${controller.counter.value}'
	),
),
```
You have to use the `value` property in the reactive variable when accessing its value, like `controller.counter.value`. 

```dart
class CounterPage extends StatelessWidget {

    final CounterController controller = Get.put(CounterController());

	
     @override
     Widget build(BuildContext context) {
	    return Scaffold(
	      body: Container(
		     child: Center(
			    child: Obx(() => Text(
	                  'Counter is ${controller.counter.value}'
	                ),
                    ),
			  ),
			),
		   );
	    }
}
```
When using the Obx, you can take advantage of GetX dependency injection. The `Put` method in GetX is used to manage your dependencies in your flutter project. And it will help you to use the same controller instance across all your child routes. After getting your CounterController instance into your widget, you can use it as the controller for the Obx.

##The Simple State Manager in GetX

The Simple state manager uses extremely low resources, since it does not use Streams or ChangeNotifier. But your widgets can listen to changes of your state, thanks to the `update()` method. After doing some changes to your state in your controller, you have to call the update method  to notify the widgets, which are listening to the state. 

```dart
class CounterController extends GetxController {
    int counter = 0;

    void increment() {
        counter++;
	   update(); // Tell your widgets that you have changed the counter
    }
}
```
You can see, we have to simply declare the state variable as we normally do. Unlike reactive variables, you don't need to transform your state variables into something else (In reactive approach we need to declare reactive variables using .obs). 

###GetBuilder

The `GetBuilder` widget will update your view based on your state changes.

```dart
GetBuilder<CounterController>(
    init: CounterController(),
    builder: (controller) => Text(
             'Counter: ${controller.counter.value}'
    ),
), 
```

If you use a controller for the first time in your GetBuilder, then you have to initialize it first. After that you don't have to start the same controller again in another GetBuilder. Because all GetBuilders that depend on the same controller will share the same controller instance across your application. This is how the simple state manager consumes extremely less memory. In simple terms, if 100 GetBuilders use the same controller, they will share the same controller instance. There won't be 100 instances for the same controller.

```dart
class CounterController extends GetxController {
    int counter1 = 0;
    int counter2 = 0;

    void incrementCounter1() {
        counter1++;
        update();
    }

    void incrementCounter2() {
        counter2++;
        update();
    }
}
```

```dart
GetBuilder<CounterController>(
    init: CounterController(), /* initialize CounterController if you use 
                                     it first time in your views */
    builder: (controller) {
             return Text('Counter 1: ${controller.counter1.value}'); 
       }
    ),



   /* No need to initialize CounterController again here, since it is 
      already initialized in the previous GetBuilder */
GetBuilder<CounterController>(                            
    builder: (controller) {
             return Text('Counter 2: ${controller.counter2.value}'); 
       }
    ),
```

If you use GetBuilder, you no longer need to use StatefulWidgets in your application. You can handle your ephemeral state(UI state) in a cleaner and easy way using GetBuilder than SetState.  In simple terms, you can make your class as StatelessWidget and update the specific components by only wrapping them in GetBuilder. That's all. You don't need to waste your resources by making the whole class as a StatefulWidget.


##MixinBuilder : Mix your both state managers

The `MixinBuilder` mixes the both state managers. So you can use both `Obx` and `GetBuilder` together. But keep it mind, MixinBuilder consumes more resources than the other two approaches. If you really care about your application performance, try to use the MixinBuilder as little as possible. The use cases of MixinBuilder, on the other hand, are rare.

```dart
class CounterController extends GetxController {
    var counter1 = 0.obs; // For reactive approach
    int counter2 = 0;    // For simple state management approach

    void incrementCounter1() {
      counter1.value++;
    }

   void incrementCounter2() {
       counter2++;
       update();
    }
}
```
```dart
MixinBuilder<CounterController>(
    init: CounterController(),
    builder: (controller) => Column(
       children: [
           Text('Counter 1: ${controller.counter1.value}), 
                         // For reactive approach

           Text('Counter 2: ${controller.counter2}') 
                       // For simple state management approach
        ]
    ),
),
```

##StateMixin

You can use the `StateMixin` to handle your UI state in a more efficient and clean way, when you perform asynchronous tasks.
Let's say your application is going to fetch some products from a cloud server. So this asynchronous task will take a certain amount of time to complete. So your application status and the state will be changed according to the response of your asynchronous task.

**Loading status :** Until you get the response, you have to wait.
**Success status :** You get the expected response.
**Error status :** Some errors can be happened when performing the asynchronous task

These are the main status of your application when performing an asynchronous task. So the StateMixin helps to update your UI according to these status and state changes.

You have to simply add StateMixin to your controller using `with` keyword. You should also specify the type of state to be handled by the StateMixin, such as `StateMixin<List<Product>>`. 

```dart
class ProductController extends GetxController with StateMixin<List<Product>> {}
```
And also `RxStatus` class provides defined status to use with the StateMixin.

```dart
RxStatus.loading();
RxStatus.success();
RxStatus.empty();
RxStatus.error('error message');
```
The StateMixin provides the `Change()` method and it changes the State according to our asynchronous task response. You have to just pass the new state and the status.
```dart
change(newState, status: RxStatus.success());
```
Let's take an example.

```dart
class ProductController extends GetxController with StateMixin<List<Product>>{

	@override
	void onInit() {
	    fetchProducts();
	    super.onInit();
	}

    
	void fetchProducts() async {
          // You can  fetch products from remote server
	    final response = await fetchProductsFromRemoteServer(); 
       
	    If(response.hasData) {
	        final data = response.data;
		   //..
              // Successfully fetched products data
		  change(data, status: RxStatus.success()); 	
	
          } else if(response.hasError) {
              // Error occurred while fetching data
		  change(null, status: RxStatus.error('Something went wrong')); 

          } else {
              // No products data
	      change(null, status: RxStatus.empty());
	    }
	}
}
```

As you can see, after getting a successful response with data, I have changed the state by passing the response `data` and the `RxStatus.success` to the change() method. Likewise I have changed the state according to error response and empty response data.

```dart
class ProductsPage extends StatelessWidget {


    // Get a ProductController instance
    final ProductController controller = Get.put(ProductController());
	
	
    @override 
    Widget build(BuildContext context) {
        return Scaffold(
            // app bar
			
            body: controller.obx(

                (productsState) => ShowProductList(productsState),
				
			onLoading: CustomLoadingIndicator(),
				
			onEmpty: Text('No products available'),
				
			onError: (error) => Text(error),

			)
		);
	}
}
```
`controller.obx()` widget will change your UI according to the changes of the status and the state. 
When an asynchronous task starts (fetching products from the server), the `obx` widget will show the default loading indicator. You can also set the `CustomLoadingIndicator()` to the `onLoading` property. 
After successfully fetching data, the `obx` widget will render the data to the UI using the `ShowProductList()` custom widget. 
If something goes wrong, by default `obx` widget will render a Text widget to show the error. And also you can provide the custom error widget to the `onError` property. (Note that the `controller.obx()` widget in here is completely different from what you have learned in reactive `Obx()`).

##Conclusion 

When you use GetX in your next project, you will realize how awesome it is. The primary goal of this article is to provide a quick overview of GetX.  The top priority of the GetX is to improve your application performance while managing the state in a simple and well organized way. 

You can read more about Getx from [official documentation](https://github.com/jonataslaw/getx/blob/master/documentation/en_US/state_management.md).