Contents
Outline
In Flutter, to manage the state, we should use the StatefulWidget
or InheritedWidget
. However, when we mange the complex state, we should use the Bloc
pattern or packages like the Provider
.
In this blog post, I will introduce the GetX
package that is most used package for state management in Flutter. You can see full source code of this blog post on the link below.
GetX
GetX
is best known as a state management package, but it actually has many other features. If you use GetX in Flutter, you can use Route, localization, getting screen size, and calling API features.
- [GetX] State management
- [GetX] Route management
- [GetX] Dependency management
- [GetX] Localization
- [GetX] Theme
- [GetX] BottomSheet
- [GetX] Dialog
- [GetX] Snackbar
- [GetX] Platform and device info
In this blog post, I will show you how to manage the state by GetX in Flutter.
GetX installation
To check how to use GetX in Flutter, execute the command below to create a new Flutter project.
flutter create state_management
And then, execute the command below to install the GetX
package.
flutter pub add get
In this blog post, we’ll refactor the basic project that is created by the Flutter command to manage the state by GetX.
GetX configuration
To use GetX
in Flutter, we should use GetMaterialApp
instead of MaterialApp
. To check this, open the lib/main.dart
file and modify it like the below.
import 'package:get/get.dart';
...
class MyApp extends StatelessWidget {
...
@override
Widget build(BuildContext context) {
return GetMaterialApp(
...
);
}
}
State management
GetX provides two ways to manage the state.
- Simple state management
- Responsive state management
Simple state management
To use the simple state management in GetX, create the lib/controller/count_controller.dart
file and modify it like the below.
import 'package:get/get.dart';
class CountController extends GetxController {
int count = 0;
void increment() {
count++;
update();
}
}
To manage the state by GetX, we should create a class that extends GetxController
.
class CountController extends GetxController {
...
}
And then, define a variable that is for the state.
class CountController extends GetxController {
int count = 0;
...
}
Lastly, make a function to update the state.
class CountController extends GetxController {
...
void increment() {
count++;
update();
}
}
In the simple state management, after updating the state, you should call update()
function to notify the state is changed. If you don’t call update()
function, the state is changed but the screen that uses the state is not updated.
Next, let’s use the GetX
controller that we’ve made. Open the lib/main.dart
file and modify it like the below.
import 'package:get/get.dart';
import 'controller/count_controller.dart';
...
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
final controller = Get.put(CountController());
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
GetBuilder<CountController>(builder: (controller) {
return Text(
'${controller.count}',
style: Theme.of(context).textTheme.headline4,
);
}),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
We’ll manage the state by GetX, so we don’t need to use the StatefulWidget
anymore. So, we can change the MyHomePage
to the StatelessWidget
.
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
...
}
To manage the state by GetX, we should create a GetX controller, and then, we should register it by using Get.put
. After registring, we can use the controller to manage the state.
class MyHomePage extends StatelessWidget {
...
@override
Widget build(BuildContext context) {
final controller = Get.put(CountController());
...
}
}
In the simple state management, you should use GetBuilder
to detect the state is changed and apply it to the screen. If you don’t use GetBuilder
, the widget can’t detect the state is changed, so the widget will not be updated.
GetBuilder<CountController>(builder: (controller) {
return Text(
'${controller.count}',
style: Theme.of(context).textTheme.headline4,
);
}),
To change the state value created by GetX, we need to call the increment
function. To call the increment
function, bind it to the onPressed
of the FloatingActionButton
widget like the below.
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
Now, when you start the project and press the action button, you can see the count value is increased well on the screen. Like this, you can decide the updating timing of the state by using the update()
function in the simple state management of GetX.
Responsive state management
The responsive state management detects the state is changed by the internal logic and appy it to the screen unlike the simple state management that we need to use the update
function to notify the state is changed.
To check this, modify the lib/controller/count_controller.dart
file like the below.
import 'package:get/get.dart';
class CountController extends GetxController {
final count = 0.obs;
void increment() {
count.value++;
// count(count.value + 1);
}
}
Unlike the simple state management, we need to use .obs
to define the state variable. The variable type won’t be the simple type like int
or string
. It will be the responsive variable type like RxInt
or RxString
.
you can use two ways to update responsive state variables like the below.
count.value++;
// or
count(count.value + 1);
Next, to use the responsive state management, modify the lib/main.dart
file like the below.
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
final controller = Get.put(CountController());
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Obx(
() => Text(
"${controller.count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
To detect the state is changed, we should use Obx
instead of GetBuilder
in the responsive state management.
Obx(
() => Text(
"${controller.count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
Now, when you execute the program and press the action button, you can see the count value is increased well on the screen.
LifeCycle
In the StatefulWidget, you can use the lifecycle methods. As same it, you can use the lifecycle methods in the GetXController
like the below.
class CountController extends GetxController {
@override
void onInit() {
super.onInit();
}
@override
void onClose() {
super.onClose();
}
}
- onInit: When the controller is created, it is called.
- onClose: When the controller is removed from the memory, it is called.
Worker
Worker detects the state is changed, and calls a callback function when the state is changed.
ever(count, (_) => print("called every update"));
once(count, (_) => print("called once"));
debounce(count, (_) => print("called after 1 second after last change"), time: Duration(seconds: 1));
interval(count, (_) => print("called every second during the value is changed."), time: Duration(seconds: 1));
- ever: Called whenever the responsive state value changes.
- once Called only once, when the responsive state value changes for the first time.
- debounce: It works like
debounce
. Called if there is no change for a certain amount of time since the last change. - interval: It works like
interval
. Called at regular intervals while the status value is being changed.
You can use Worker when the class or controller is created. So, you can use it on onInit of the controller, class construct, and initState of the StatefulWidget.
Get.find
In above examples, we use the controller created by Get.put
to use the state value.
final controller = Get.put(CountController());
If you want to use the state value in the child widget or change it, how can we do it? Of course, we can pass the controller via the parameter of the widget like the below.
CustomWidget(controller: controller)
However, GetX provides Get.find
to access the controller easily like the below.
Get.find<CountController>()
To check this, modify the lib/main.dart
file like the below.
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
Get.put(CountController());
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Obx(
() => Text(
"${Get.find<CountController>().count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: Get.find<CountController>().increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Before, we’ve used the controller that is created by Get.put(CountController());
to access the state value, but now, we use Get.find<CountController>()
to access the state value like the below.
Obx(
() => Text(
"${Get.find<CountController>().count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
Also, we can use Get.find
to call the function that changes the state.
floatingActionButton: FloatingActionButton(
onPressed: Get.find<CountController>().increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
You can use Get.find
anywhere to access the controller that is created by Get.put
. In the example here, we use Get.find
to access the controller in the same file, but you can use it in the other child widgets. The important point is, you should register the controller by Get.put
first, and then, you can use Get.find
to access the controller. If you try to access the controller that is not registered, the error will occur.
Get.isRegistered
We can use Get.find
to use the controller that is created by Get.put
, and if the controller is not registerd, the error will occur. To solve this error, you can use Get.isRegistered
to check the controller is registered or not.
Get.isRegistered<CountController>()
If the controller is registered, it returns true
. If the controller is not registered, it returns false
.
static get to
When you manage the state by GetX, you’ll often access state values by using Get.find
. So, the below pattern that uses static
is often used in GetX
.
static CountController get to => Get.find<CountController>();
To check this, modify the lib/controller/count_controller.dart
file like the below.
import 'package:get/get.dart';
class CountController extends GetxController {
static CountController get to => Get.find<CountController>();
final count = 0.obs;
void increment() {
count.value++;
// count(count.value + 1);
}
}
And then, modify the lib/main.dart
file like the below.
class MyHomePage extends StatelessWidget {
final String title;
const MyHomePage({Key? key, required this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
Get.put(CountController());
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Obx(
() => Text(
"${CountController.to.count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: CountController.to.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Before we’ve accessed the state value by the controller
variable, now we can access it by the static
like the below.
Obx(
() => Text(
"${CountController.to.count.value}",
style: Theme.of(context).textTheme.headline4,
),
),
...
floatingActionButton: FloatingActionButton(
onPressed: CountController.to.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
In GetX, this pattern is often used, so it’s better to remember it well.
Complated
Done! we’ve seen how to manage the state by GetX
in Flutter. Now, let’s try to manage the state By GetX instead of StatefulWidget.
Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me!
App promotion
Deku
.Deku
created the applications with Flutter.If you have interested, please try to download them for free.