Contents

    Guides

    GetX state management in Flutter

    Muyiwa Femi-ige

    Muyiwa Femi-ige is a Web and app development enthusiast who has a knack for putting code into a well-documented format.

    Published on

    July 11, 2022
    GetX state management in Flutter

    To get started with the proceedings, we will give a quick run-through of the aim of this tutorial:

    • What is state management and its different approaches in Flutter and other technologies.
    • What is GetX?
    • Project: Demonstrating a sample state management process using an HTTP request to fetch posts from an online API utilizing the HTTP package.

    Prerequisite

    • Knowledge of state management or must have used any technology that uses state management is helpful.
    • Must know Flutter and dart with workable knowledge with them.

    State management

    What is state?

    According to Matt Caroll, state is the "behavior of an app at any given moment." Take this analogy into consideration — "you have a simple lightbulb; when you switch it on, you are changing the state of that light bulb from off to on." So, state is the collection of variables at a certain amount of time and the moment a program shows its current contents.

    Though this is a flutter topic, state management is not particular alone to Flutter. In fact, redux from React(formerly called flux) is one of the most famous state management technologies. Redux is what you term a "uni-directional" workflow, and we have an image representing what that means:

    An example of a redux workflow

    Now let us talk about state management in Flutter. The first thing to note is that Flutter is declarative, meaning we have UI displayed as a function from the state. Thus, when we run a specific function with the state, we get our UI as a relation. GetX comes in since it was built as a simple state management solution in flutter application development.

    What is GetX?

    When working with Flutter, you build your application using widgets that form tree-like structures where a single widget can have child widgets that can have other sub widgets. There are scenarios where a change or an action in one widget needs to effect a change in another widget elsewhere. For widgets in the same tree but with a different hierarchy, it is possible to pass the changes through the multiple generations of widgets to get the variable effect of the change you need in the widget you need to make the change. For widgets not in the same tree, you have to introduce a new widget and make them share a standard parent widget. It can soon become a nightmare when the widget tree is deep, or you need to make simple updates.

    GetX comes as a "lightweight and powerful" flutter package that offers developers the ability to handle complex state management, route management, and integrate dependency injection. It focuses on three basic principles performance, productivity, and organization. GetX provides simplicity like never before. Its usage as a state management tool in your flutter application can be likened to the ease of React context API hook with only a line of code better.

    The solution GetX solves includes code readability improvement, complex app changes management and updates, and code reduction with few lines used to execute complex functionality. Compared to other state management techniques used in Flutter like the BLOC package, which is a hassle to set up, requiring a lot of lines of codes. Or Provider Widget, which has fewer lines of code than the BLOC package but can be a lot with complex functionality.

    In this tutorial, we will send an HTTP request to fetch a list of posts from an online API in our flutter app using the HTTP package to demonstrate a sample state management.

    Getting started

    1. Let's start off by creating our flutter application and have it running using the command:
    flutter run
    1. To use GetX in our application, we need to add the latest version of the GetX package to our pubspec.yaml file. Thus, we add the following dependencies below to our pubsec file. (Note: we also need to add the HTTP package as a dependency for the project's sake)
    get: ^4.6.1
    http: ^0.13.4
    
    1. Next, clear the default code in the main.dart file and replace it with the code below:
    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    import 'package:getx_test/Getx.dart';
    void main() {
     runApp(const MyApp());
    }
    class MyApp extends StatelessWidget {
     const MyApp({Key? key}) : super(key: key);
     @override
     Widget build(BuildContext context) {
      return GetMaterialApp(
       title: 'Getx State Management',
       theme: ThemeData(
        primarySwatch: Colors.blue,
       ),
       home: GetXWidget(),
      );
     }
    }

    We imported our dependencies for use in our fie in the code above. To use GetX, we will need to wrap our material app with Get by turning it into GetMaterialApp. It is only beneficial if you use the GetX route management capabilities. Next, We call a GetXWidget class to serve as our home screen.

    1. We will create another file called getX.dart (not necessarily you use the same name), and we will add the code below to it.
    import 'dart:convert';
    import 'package:get/get_rx/src/rx_types/rx_types.dart';
    import 'package:get/get_state_manager/src/simple/get_controllers.dart';
    import 'package:http/http.dart' as http;
    class BaseController extends GetxController {
     static List posts = [];
     var text = ''.obs;
     increment(val) {
      text.value = val;
     }
     Future getData() async {
      try {
       var response;
       response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos'),
         headers: {"Accept": "*/*", "Content-Type": "application/json"});
       posts = jsonDecode(response.body);
       text.value = posts[0]['title'];
       return true;
      } catch (e) {
       return Future.value(false);
      }
     }
    }
    1. Next, we want to send a request to a post endpoint to fetch a list of posts, and we assign the data fetched to a post list. All these variables and operations happen within a class that extends the GetXController. To do this, we head back to our main.dart file and add this new class to it:
    class GetXWidget extends StatefulWidget {
     const GetXWidget({Key? key}) : super(key: key);
     @override
     State createState() => _GetXWidgetState();
    }
    class _GetXWidgetState extends State {
     final BaseController baseController = Get.put(BaseController());
     int counter = 0;
     bool loading = true;
     getData() async {
      if(await baseController.getData()){
       setState(() {
        loading = false;
       });
      }
     }
     @override
     void initState() {
      getData();
      super.initState();
     }
     @override
     Widget build(BuildContext context) {
      return Scaffold(
       appBar: AppBar(
        title: const Center(child: Text('GetX State Management')),
       ),
       backgroundColor: Colors.white,
       body: loading ? Center(child: CircularProgressIndicator()) : Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
         Center(
          child: Obx(() => Text(
             baseController.text.value
          )),
         ),
         ElevatedButton(
          child: Text('Get Next'),
           onPressed: (){
             counter++;
             baseController.increment(BaseController.posts[counter]['title']);
           })
        ],
       ),
      );
     }
    }

    (Note: You can entirely append the code above underneath the main function or in another file.)

    In the code above, we initialized our basecontroller class and specified two variables; counter and loading. The counter variable will be used to programmatically induce a change in the observable variable that GetX is watching.

    The body has a column filled with two children's widgets and a text field that displays the observed variable present in the basecontroller file. A RaisedButton widget has its onPressed function calling the increment function in the basecontroller while passing the updated sting from incrementing counter to update the value in the basecontroller without calling setstate.

    An essential piece of the puzzle is updating the UI with the changes GetX has observed. We cannot achieve this without the need for the Obx() function call on the widget that uses the observable variable. It tells Flutter not to build the whole widget but only the little section of the entire widget tree where the change is needed. Thus, this increases performance and saves resources.

    So for the full code in the main.dart file, we have:

    import 'package:flutter/material.dart';
    import 'package:get/get.dart';
    import 'package:getx_test/Getx.dart';
    void main() {
     runApp(const MyApp());
    }
    class MyApp extends StatelessWidget {
     const MyApp({Key? key}) : super(key: key);
     @override
     Widget build(BuildContext context) {
      return GetMaterialApp(
       title: 'Getx State Management',
       theme: ThemeData(
        primarySwatch: Colors.blue,
       ),
       home: GetXWidget(),
      );
     }
    }
    
    class GetXWidget extends StatefulWidget {
     const GetXWidget({Key? key}) : super(key: key);
     @override
     State createState() => _GetXWidgetState();
    }
    class _GetXWidgetState extends State {
     final BaseController baseController = Get.put(BaseController());
     int counter = 0;
     bool loading = true;
     getData() async {
      if(await baseController.getData()){
       setState(() {
        loading = false;
       });
      }
     }
     @override
     void initState() {
      getData();
      super.initState();
     }
     @override
     Widget build(BuildContext context) {
      return Scaffold(
       appBar: AppBar(
        title: const Center(child: Text('GetX State Management')),
       ),
       backgroundColor: Colors.white,
       body: loading ? Center(child: CircularProgressIndicator()) : Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
         Center(
          child: Obx(() => Text(
             baseController.text.value
          )),
         ),
         ElevatedButton(
          child: Text('Get Next'),
           onPressed: (){
             counter++;
             baseController.increment(BaseController.posts[counter]['title']);
           })
        ],
       ),
      );
     }
    }
    Text button example

    Notice the text above the button changes to the next post title in the list when the button is clicked. It does this without calling setstate.

    The other operation in the code is a request to fetch post data, and it is done when the application first initializes. If the request has not yet returned true, a loading circularProgression is displayed in the center of the screen.

    Conclusion

    In this guide, we have talked about what state is, state management in other technologies, and a pictorial representation of their structure. Finally, we shed light on a small aspect of the best state management tool in Flutter, GetX.

    As its advantages come in its numbers, so do its disadvantages, as many have cited GetX as; cumbersome, bug-ridden, etc. GetX has proven itself to be a simple and reliant state management tool. Thanks for reading, and happy coding.

    Data-rich bug reports loved by everyone

    Get visual proof, steps to reproduce and technical logs with one click

    Make bug reporting 50% faster and 100% less painful

    Rating LogosStars
    4.6
    |
    Category leader

    Liked the article? Spread the word

    Put your knowledge to practice

    Try Bird on your next bug - you’ll love it

    “Game changer”

    Julie, Head of QA

    star-ratingstar-ratingstar-ratingstar-ratingstar-rating

    Overall rating: 4.7/5

    Try Bird later, from your desktop