Introduction

Whenever we start building any application in a flutter, we must decide which state management we need to use. It would be easier for you to make this decision with this blog. Here, in this tutorial: Flutter state management using GetX, I would like to explain GetX, a powerful flutter framework.

What is GetX?

State management allows you data transferring within the application. And whenever data is passed, the application’s state is updated, consequently rebuilding the system. Thus, developers have to be particularly careful about managing the state of an application because state updation may sometimes seem costly for a complex application.

Flutter traditionally provides Stateful Widget to manage states in applications. However, we need to deal with a few limitations when using Stateful Widgets.

To overcome the limitations, we can choose Flutter state management using GetX.

GetX is a powerful and lightweight solution provided by Flutter to manage states and their updation. It provides:

  • High-performance state management
  • Intelligent dependency injection
  • Quick and practical route management

Why GetX?

So, let’s dive a little deeper into why we need GetX to manage the state in the flutter app. GetX improves flutter application in three different criteria:

  • Productivity: Developers can easily implement getx state management flutter with the help of straightforward syntax. No matter how complex a code snippet can be, you can save time with GetX flutter. It increases productivity by decreasing the development time delivering maximum performance.
  • Organization and Readability: GetX decouples the View. It provides easy and uncomplicated syntax, consequently increasing the readability and format of the business logic.
  • Performance: As mentioned above, GetX focuses on how minimum resources can be consumed, which can help in improving the application performance. It doesn’t use ChangeNotifier or Streams. Look at the below RAM chart depicting various state managers.
RAM usage chart

Enough of the theory part. Let’s move forward in our Flutter state management using GetX tutorial and implement it in our application.

Implementation

Install GetX

Copy Text
flutter pub add get

Run the above command in Android studio/vs code’s terminal, and it will add the latest GetX plugin to pubspec.yaml.

We are going to cover three sections in this further tutorial
1. State management for basic counter app
2. Navigation using GetX
3. Inflating UI components without context

Put forward your requirements, and we will provide solutions!
Develop best, cost-effective, and high-performance Flutter applications with Bacancy! Stop wasting your time and connect us to hire Flutter developer!

Dependency Injection

In Getx dependency injection allows you to put/retrieve your class instance with just 1 line of code no context is required to put/retrieve your class instance.

Copy Text
Get.put(SomeClass());
Get.lazyPut(() => SomeClass());

Get.put() helps you put your instance alive throughout your app.
While Get.lazyPut helps you to manage your app memory by instantiating the class when it required by the screen and remove it later when it is no longer needed.

Getx Bindings

One of the powerful feature of this package. By using this you can connect your screen to their controllers. So whenever the screen is removed from the widget stack all the streams, variables and instances present in the controller will be cleared automatically from memory.

Project Structure

Every screen goes into feature folder
In every screen

  • Binding: List of controllers that needs to instantiate when your screen is visible
  • Controller: Business Logic
  • Repository: Contains calls to external data sources
  • Route: PageRoute (contains info about name, screen & bindings)
  • View: Your screen/widget

Flutter State Management using GetX Tutorial

Here I will create one counter app by separating UI logic and business logic using a controller, and I would use Obx for this. Don’t worry if you are not aware of all this; I am explaining all these in detail one by one.

Project Structure

You can see the project structure I have created using the recommended GetX pattern, with a view, controller, and binding class. View class handles the code for inflating the UI, which will appear on the screen. The binding class would be associated with a particular page, and in that class, we can instantiate controller classes. In controller class, we can define variables and business logic functions, in our case increment function. Also, In the main.dart, we have declared GetMaterialApp not MaterialApp so we can use all the functionalities of GetX framework.

CounterRepository Class

Copy Text
abstract class CounterRepository {
  const CounterRepository();

  Rx get count;

  void increment();
}

class CounterIncrementOneRepository extends CounterRepository {
  CounterIncrementOneRepository();

  @override
  final Rx count = 0.obs;
  final int _incrementValue = 1;

  @override
  void increment() => count.value += _incrementValue;
}

CounterRepository provides an abstraction so that you can mock the underlying data sources without affecting your presentation or business logic layer.

CounterController class

Copy Text
class CounterController extends GetxController {
  final CounterRepository repo;

  CounterController(this.repo);

  void navigateToReverseCounterScreen() {
    Get.offAllNamed(ReverseCounterRoute.name);
  }
}

Counter controller contains the repository instance and other business logic which can be used in your screen. Here I have declared the count variable with .obs, which means count is observable, and whenever there is a change in this value, we can listen to that value via controller class. Unlike providers, we do not need to call notifylisteners. In the increment function, I am simply incrementing the count variable value.

CounterBinding class

Copy Text
class CounterBinding implements Bindings {
  const CounterBinding();

  @override
  void dependencies() {
    Get.lazyPut(() => CounterController(CounterIncrementOneRepository()));
  }
}

As discussed earlier Get.lazyPut() helps you to instantiate your controller when it is needed like when your screen is visible to the user and remove the controller when your screen is removed from the widget stack.

CounterRoute Class

Copy Text
class CounterRoute {
  const CounterRoute._();

  static const String name = '/';
  static const Bindings binding = CounterBinding();
  static const Widget screen = CounterScreen();

  static final GetPage page = GetPage(
    name: name,
    binding: binding,
    page: () => screen,
  );

  static GetPageRoute get pageRoute => GetPageRoute(
        settings: const RouteSettings(name: name),
        binding: binding,
        page: () => screen,
      );
}

Counter route contains your route-related info like name, binding and screen.

CounterScreen Class

Copy Text
class CounterScreen extends StatelessWidget {
  const CounterScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final CounterController controller = Get.find();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: controller.navigateToReverseCounterScreen,
              child: const Text('Go to reverse counter screen'),
            ),
            const SizedBox(height: 16),
            const Text(
              'You have pushed the button this many times:',
            ),
            Obx(
              () => Text(
                '${controller.repo.count.value}',
                style: Theme.of(context).textTheme.headline4,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.repo.increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

final CounterController controller = Get.find();

The above line provides the reference of the controller.

MainPart Class

To make your getx binding work you need to pass route info in GetMaterialApp -> getPage property. Here we will use the CounterRoute class that we already created
main.dart

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Getx Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: CounterRoute.name,
      getPages: [
        CounterRoute.page,
        ReverseCounterRoute.page,
      ],
    );
  }
}

For testing getx binding I have created a second screen same as counter but it is a reverse counter so when a user clicks on minus button counter will decrement

Here are all the files used in reverse counter

Flutter GetX Class

Reverse Counter Repository (Only change from counter repository)

Copy Text
abstract class ReverseCounterRepository {
  const ReverseCounterRepository();

  Rx get count;

  void decrement();
}

class ReverseCounterDecrementOneRepository extends ReverseCounterRepository {
  ReverseCounterDecrementOneRepository();

  @override
  final Rx count = 0.obs;
  final int _decrementValue = 1;

  @override
  void decrement() => count.value -= _decrementValue;
}

Here you can see that this repository decrements the value by one. This is the only change done in reverse counter screen.

Reverse Counter Repository

Here you can see the logs CounterController gets created and initialized when user navigates to counter screen and gets deleted from memory when user moves to new screen

Same goes for reverse counter controller ReverseCounterController gets created and initialized when user navigates to reverse counter screen and gets deleted from memory when user moves to new screen

NamedNavigation Class

Add “Get” before your MaterialApp, turning it into GetMaterialApp
And assign initialRoute and getPages

Copy Text
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      title: 'Flutter Getx Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: CounterRoute.name, // Route for home page
      getPages: [ // Routes/Screen available throughout app
        CounterRoute.page, 
        ReverseCounterRoute.page,
      ],
    );
  }
}

Here CounterRoute.page is 

static final GetPage page = GetPage(
    name: name, // This name is used to assign named route 
    binding: binding,
    page: () => screen,
  );


To navigate to a named route use 
Get.toNamed('/some_page');
Get.offNamed('/some_page');
Get.offAllNamed('/some_page');
Get.offNamedUtil('/some_page');

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

Using the above syntax in the build method, I have defined the controller class. Text button will call the increment method defined in the controller class and Text which will show the updated value of count. But the main thing you can see is the text widget is wrapped with Obx, which means it can get the value of the observable variable; without Obx, the value would not get reflected.

Here I have used one simple example of counter application to understand all the classes, structure, and state management easily. We can achieve many more using GetX flutter using following this observable pattern and writing cleaner code.

Let’s dive into the navigation part.

Navigation using GetX

In the screenshot attached in the state management block, we have also created a page name home. So let’s say we need to go to the home page from the counter class on one button click. We can simply call the GetX navigation block, as shown below.

Get.to(HomeView());

Pretty simple. Isn’t it? Rather than calling up a lot of boilerplate code, we can simply call this and move to a different screen. Moreover, there are different options to redirect to another page too.

For instance, you can simply replace the home screen with a currently open screen below. It means the current screen which would get replaced won’t be in the stack.

Get.off(HomeView());

And, if we need to remove all previous stacks, we can call Get.off(HomeView());

Get.offAll(HomeView());

Apart from that, we can pass data between routes and show animation before opening another route and open a screen as a dialog using flutter GetX.

Now let’s move to our final point of Inflating UI components without context.

Inflating UI Components without Context

Traditionally, to open a dialog or bottom sheet. If you have a separate file that handles common widgets, we also need to pass context to that class. But with GetX, it is not the case. We can simply inflate that kind of UI block without using context and in an easier way.

To show snackbar

Copy Text
Get.snackbar('This is snackbar', 'This is snackbar message',  backgroundColor: Colors.red);
Show Snackbar

To show dialog

Copy Text
Get.defaultDialog(
   title: 'This is dialog',
   middleText: 'This is middle text',
   buttonColor: Colors.green,
   textCancel: "cancel",
   textConfirm: "confirm");
Show Dialog

To show bottom sheet

Copy Text
Get.bottomSheet(
 Container(
   child: Wrap(
     children: [
       ListTile(
         leading: Icon(Icons.wb_sunny_outlined),
         title: Text("Light theme"),
         onTap: () => {Get.changeTheme(ThemeData.light())},
       ),
       ListTile(
         leading: Icon(Icons.wb_sunny),
         title: Text("Dark theme"),
         onTap: () => {Get.changeTheme(ThemeData.dark())},
       )
     ],
   ),
 ),
 backgroundColor: Colors.green
);
Theme

I think this is it for GetX. You can go through the below official link to explore more about GetX.
https://pub.dev/packages/get

With the Git Cli plugin, you can make the project structure more trouble-free. You can check out the link below.
https://pub.dev/packages/get_cli

Conclusion

That’s it for Flutter state management using GetX tutorial. If you are a flutter enthusiast, the Flutter tutorials page is for you! Try the tutorials and start implementing them in your application. Write us back if you have any suggestions, queries, or questions.

Solve State Management Problems in Flutter

Implement powerful state management library – GetX to manage states, make routing, and perform intelligent dependency injection without sacrificing app performance.

Prevent the Dependency Nightmares

Get In Touch

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.