Getx — Flutter (Part -1) (2024)

Getx — Flutter (Part -1) (2)

Hello Medium readers. Today we are going to discuss about Getx in Flutter.

GetX is an extra-light and powerful solution for Flutter. It combines high-performance state management, intelligent dependency injection, and route management quickly and practically.

Performance

GetX does not use Streams or ChangeNotifier so because of this Getx is focused on performance and use minimum consumption of resources.

In GetX developers should be concerned with removing controllers from memory. With GetX this is not necessary because resources are removed from memory automatically when they are not used by default. If you want to keep it in memory, you must explicitly declare “permanent: true” in your dependency.

Decoupling is a state of an IT environment in which two or more systems somehow work or are connected without being directly connected

GetX allows 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.

We don’t need context to access your controllers/blocs through an inheritedWidget so you completely decouple your presentation logic and business logic from your visualization layer.

We do not need to inject our Controllers/Models/Blocs classes into your widget tree through MultiProvider. GetX uses its own dependency injection feature, decoupling the DI from its view completely.

GetX does not use Streams or ChangeNotifier like other state managers. Why? In addition to building applications for android, iOS, web, windows, macos and linux, with GetX you can build server applications with the same syntax as Flutter/GetX. In order to improve response time and reduce RAM consumption, we created GetValue and GetStream, which are low latency solutions that deliver a lot of performance, at a low operating cost.

Some state managers are complex and have a lot of boilerplate. With GetX you don’t have to define a class for each event, the code is highly clean and clear, and you do a lot more by writing less.

With GetX, even nested widgets are respected. If you have Obx watching your ListView, and another watching a checkbox inside the ListView, when changing the CheckBox value, only it will be updated, when changing the List value only the ListView will be updated.

  • It only reconstructs if its variable REALLY changes: GetX has flow control, that means if you display a Text with ‘Gaurav’, if you change the observable variable to ‘Gaurav’ again, the widget will not be reconstructed. That’s because GetX knows that ‘Gaurav’ is already being displayed in Text, and will not do unnecessary reconstructions.

There are some points we need to keep in our mind before using GetX.

  • We won’t need to create StreamControllers.
  • We won’t need to create a StreamBuilder for each variable
  • We will not need to create a class for each state.
  • We will not need to create a get for an initial value.

For instance we have a name variable and we want to change it everytime. All widgets that use it are automatically changed.

var name = 'Jonatas Borges';

To make it observable, you just need to add “.obs” to the end of it:

var name = 'Jonatas Borges'.obs;

===> we know a Widget can only be changed if it is inside a function, because static classes do not have the power to "auto-change".

You will need to create a StreamBuilder subscribe to this variable to listen for changes, and create a "cascade" of nested StreamBuilder if you want to change several variables in the same scope, right?

No, you don’t need a StreamBuilder , but you are right about static classes.

we usually have a lot of boilerplate when we want to change a specific Widget. With GetX you can also forget about this boilerplate code.

Obx (() => Text (controller.name));

You are just passing that Widget through an arrow-function into an Obx() (the "Observer" of the Rx).

Obx is pretty smart, and will only change if the value of controller.name changes.

Let’s see a simple example:

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

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

class Controller extends GetxController {
// Define an observable variable
var count = 0.obs; // ".obs" makes it observable

// Increase the count
void increaseCount() {
count++;
}
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Obx Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Display the current count using Obx
Obx(() {
final Controller controller = Get.find<Controller>();
return Text('Count: ${controller.count}');
}),

// Button to increase the count
ElevatedButton(
onPressed: () {
final Controller controller = Get.find<Controller>();
controller.increaseCount();
},
child: Text('Increase Count'),
),
],
),
),
),
);
}
}

GetBuilder can be wrapped around any widget to make it communicate with the controller’s methods and variables. We’ll be able to call functions, monitor state changes, and so on.

 GetBuilder<Controller>( // specify type as Controller
init: Controller(), //initialize with the Controller
builder: (value) => Text(
'${value.counter}', // value is an instance of Controller.
),
),

GetBuilder<Controller>( // no need to initialize Controller ever again, just mention the type
builder: (value) => Text(
'${value.counter}', // counter is updated when increment() is called
),
),

Get Builder is simple to have the least CPU impact, and just to fulfill a single purpose (a State rebuild) and spend the minimum resources possible.

We have 3 ways to turn a variable into an “observable”.

  1. Using RxType:
// initial value is recommended, but not mandatory
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});

2. use Rx and use Darts Generics, Rx<Type> :

final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0);
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});

// Custom classes - it can be any class, literally
final user = Rx<User>();

3. just add .obs as a property of your value :

final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;

// Custom classes - it can be any class, literally
final user = User().obs;

Let’s for instance we have created some final values in our controller file.

final count1 = 0.obs;
final count2 = 0.obs;
int get sum => count1.value + count2.value;

Then in our presentation layer we will do :

// view file
GetX<Controller>(
builder: (controller) {
print("count 1 rebuild");
return Text('${controller.count1.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 2 rebuild");
return Text('${controller.count2.value}');
},
),
GetX<Controller>(
builder: (controller) {
print("count 3 rebuild");
return Text('${controller.sum}');
},
),

In the above code If we increment count1.value++ , it will print:

  • count 1 rebuild
  • count 3 rebuild

here count 2 will not rebuild.

because count1 has a value of 1 , and 1 + 0 = 1 , changing the sum getter value.

If we change count2.value++ , it will print:

  • count 2 rebuild
  • count 3 rebuild

here count 1 will not rebuild

because count2.value changed, and the result of the sum is now 2 .

“ever” is used as a part of the reactive programming paradigm provided by GetX for handling observable variables. When you mark a variable as ever, it allows you to react to changes in that variable.

  • We can convert our class values to obs.
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
  • we can also convert the entire class to be an observable.
class User {
User({String name, int age});
var name;
var age;
}

// when instantianting:
final user = User(name: "Camila", age: 18).obs;

we don’t need to use “.value” with lists.

// On the controller
final String title = 'User Info:'.obs
final list = List<User>().obs;

// on the view
Text(controller.title.value), // String need to have .value in front of it
ListView.builder (
itemCount: controller.list.length // lists don't need it
)

But what if When we are making our own classes observable, there is a different way to update them:

// on the model file
// we are going to make the entire class observable instead of each attribute
class User() {
User({this.name = '', this.age = 0});
String name;
int age;
}

// on the controller file
final user = User().obs;
// when you need to update the user variable:
user.update( (user) { // this parameter is the class itself that you want to update
user.name = 'Jonny';
user.age = 18;
});
// an alternative way of update the user variable:
user(User(name: 'João', age: 35));

// on view:
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"))
// you can also access the model values without the .value:
user().name; // notice that is the user variable, not the class (variable has lowercase u)

Workers triggering specific callbacks when an event occurs. we have 4 types of worker in GetX.

/// Called every time `count1` changes.
ever(count1, (_) => print("$_ has been changed"));

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

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

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

All workers returns a Worker instance, that you can use to cancel ( via dispose() ) the worker.

Let’s see each worker one by one:

  • ever

is called every time the Rx variable emits a new value.

  • everAll

Much like ever , but it takes a List of Rx values Called every time its variable is changed. That's it.

  • once

‘once’ is called only the first time the variable has been changed.

  • debounce

‘debounce’ is very useful in search functions, where you only want the API to be called when the user finishes typing. If the user types “Jonny”, you will have 5 searches in the APIs, by the letter J, o, n, n, and y. With Get this does not happen, because you will have a “debounce” Worker that will only be triggered at the end of typing.

  • interval

‘interval’ is different from the debouce. debouce if the user makes 1000 changes to a variable within 1 second, he will send only the last one after the stipulated timer (the default is 800 milliseconds). Interval will instead ignore all user actions for the stipulated period. If you send events for 1 minute, 1000 per second, debounce will only send you the last one, when the user stops strafing events. interval will deliver events every second, and if set to 3 seconds, it will deliver 20 events that minute.

Workers should always be used when starting a Controller or Class, so it should always be on onInit (recommended).

That’s it for the first part of GetX. We will see more topics and functionalities of Getx in next medium article.

Clap 👏 If this article helps you.

See you all again soon!

Getx — Flutter (Part -1) (2024)

References

Top Articles
Latest Posts
Article information

Author: Lidia Grady

Last Updated:

Views: 5998

Rating: 4.4 / 5 (65 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Lidia Grady

Birthday: 1992-01-22

Address: Suite 493 356 Dale Fall, New Wanda, RI 52485

Phone: +29914464387516

Job: Customer Engineer

Hobby: Cryptography, Writing, Dowsing, Stand-up comedy, Calligraphy, Web surfing, Ghost hunting

Introduction: My name is Lidia Grady, I am a thankful, fine, glamorous, lucky, lively, pleasant, shiny person who loves writing and wants to share my knowledge and understanding with you.