Flutter Getx App Tutorial Explained

Flutter Getx App Tutorial Explained

    Flutter state management 

    There are many packages to main flutter state managent. Getx and Bloc are two them and they are both very poplular.There are others like Provider state management and Riverpod state management .Getx is relatively new but easy to use for beginners and Bloc is more mature since it's older but it's also a little difficult to start with.

    In this tutorial you will learn how to build a flutter getx app using flutter getx package step by step. We have covered getx route, state management, passing arguments, named routes, creating controllers and dependency injection. 

    Download the starter code from the link below

    https://www.dbestech.com/flutter_getx.zip

    The video tutorial

    Basics of Getx

    Getx Controller

    Take a look at the common type of controller in Getx. It's just like any other controller in programming language. If you are familiar with PHP, C or C++, Java, this controller would look very familiar to you.

    class HomeController extends GetxController {
      final count = 0.obs;
    
      @override
      void onInit() {
        super.onInit();
      }
    
      @override
      void onReady() {}
    
      @override
      void onClose() {}
    
      void increment() => count.value++;
    }
    

    To use Getx, to make your variables react active or stateful you need your class to extend GetxController. Like you saw in the above example. 

    In controller's onInit() method you do initialization like data from network or loading data from JSON files or navigation.

    You may also override onReady() method. onReady() method gets called after onInit() method. In this simple example, we are not going to use them.

    In the controller your varables could be obs or non obs type. In the above controller, our variable is obs type. We also declared a custom method name increment(), it increases the value of count variable when you will call this method from the UI

    See GetX Advanced Tips Here

    Dependency Injection

    I think you have seen the below syntax many times in flutter Gext. If not see now. It will save your day.

    AnyController controller = Get.put(AnyController());

    This is called dependency injecting. It means putting or injecting a class or a controller (controllers are class too) into another class. 

    In getx you can inject the controller like below

    // home_view
    class HomePage extends StatelessWidget {
      HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
    
      //dependency injection
      AnyController controller=Get.put(AnyController());
    
    
        return Scaffold(
          body: Container(
            child: Obx(() => Center(child: Text(controller.count.toString()))),
          ),
        );
      }
    }

    Because we injected the dependency, so now we can access the count variable using controller instance of AnyController.

    onInit() vs onReady()

    onInit() gets called at the very early stage of Getx life cycle. It also gets called before the build method of Flutter framework. 

    onInit() gets once the controller is created

    onReady() gets called after onInit() and it also gets called after build method gets called first.

    class HomePage extends GetView<UserController> {
      const HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        print("on build method....${controller.name.value}");
    
        return Scaffold(
            body:SafeArea(
              child:  Center(
                child: Obx(()=>Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(
                      "${controller.name.value}",
                      style: TextStyle(fontSize: 25),
                    ),
                    Text(
                      "${controller.description.value}",
                      style: TextStyle(fontSize: 25),
                    )
                  ],
                ))
    
              ),
            ));
      }
    }
    
    class UserController extends GetxController{
      var name = "".obs;
      var description ="He is a freelancer".obs;
    
      @override
      void onInit(){
        super.onInit();
        name.value="ahmed";
    
        print("on onInit ...${name.value}");
      }
    
      @override
      void onReady(){
        super.onReady();
        name.value="dylan";
    
        print("on onReady ...${name.value}");
      }
    }

    Obx(()=>) or GetBuilder()

    If you have obs varaibles in your controller then you should use Obx(()=>) in your view around obs widgets(the widgets that use the obs variables). If you did not use obs variables and used updated() method in the controller, then you should use GetBuilder() in your view.

    // home_view
    class HomePage extends GetView<HomeController> {
      HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Container(
            child: Obx(() => Center(child: Text(controller.count.toString()))),
          ),
        );
      }
    }
    

    See how Obx(()=>) has been used. You can also get instance of controller and use the obs variable inside view.

    // home_view
    class HomePage extends StatelessWidget {
      HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
      var controller=Get.put(HomeController());
        return Scaffold(
          body: Container(
            child: Obx(() => Center(child: Text(controller.count.toString()))),
          ),
        );
      }
    }
    

    Of course you can also do conditional check in Obx() like below

    Obx((){
         if(...){
         
        }else{
    
       }
    
    })

    Do remember Obx() only works with the variable of obs type. If your widget contains obs and non obs type variable together, then your Obx() won't be reactive, meaning it won't update the value as you expect.

     

    If you did not use obs then you should GetBuilder around your widget to get access to the controller variable

    class HomePage extends GetView<HomeController> {
      HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        HomeController controller = Get.put(HomeController());
    
        return BaseScaffold(
          appBar: MyAppBar(
            centerTitle: true,
            title: MyTitle('首页'),
            leadingType: AppBarBackType.None,
          ),
          body: Container(
            child: GetBuilder<HomeController>(builder: (controller) {
              return Center(child: Text(controller.count.toString()));
            }),
          ),
        );
      }
    }
    

    Of course you need to initialize HomeController first.  You see, how Center widget is wrapped with GetBuilder(). 

    Personally I use Obx(()=>) more than GetBuilder() since, it's cleaner and with the help of instantiated controller, you can access them anywhere in your view.

    Anyway, whenever you use obs or update method inside your controller, flutter does the rebuild of ui and state, so you are able to see the changes.

    Getx make variables observable

    final name = ''.obs;
    
    final isLogged = false.obs;
    
    final count = 0.obs;
    
    final balance = 0.0.obs;
    
    final number = 0.obs;

    These are some of the ways, you can delcare observable variables in Getx.

    But if you don't want to use obs end of the variable then you can update() method.

    For exmaple look at this

    class HomeController extends GetxController {
      final count = 0;
    .......................
     ......................
     ........................
    
      void increment() {
          count++;
         update();
      }
    }
    

    This update() inside increment() function makes it observable.

    Getx add two or more variables

    class HomeController extends GetxController {
      final count = 0;
      final newCount=0;
    .......................
     ......................
     ........................
    int get sum=>count+newCount;
    
      void increment() {
          count++;
         update();
      }
      void incrementNew() {
          newCount++;
         update();
      }
    }

    It's pretty easy to add two variables. In case, you add two obs type variable you can do like below

    int get sum=>count.value+newCount.value;

    obs type variables setting or getting values is always done with .value;

    Getx make Lists observable

    To make a flutter list observable you need to add the obs word with the list. You need to declare a list variable first.

    There are three different ways you can do it.

    var myList = [].obs;
    var myList=<Model>[].obs
    RxList<Model> myList=<Model>[].obs

    Any one more them should work based on your skd version. Learn about Lists and Maps here

    Getx make a Map observable

     var _categories = {
        "Shopping": false,
        "Reading": true,
        "Exercise": true,
      }.obs;

     

    And if you want to show the list inside ListView then you need to do like below

    Expanded(
          child:Obx((){
            return ListView.builder(
                itemCount: _taskController.myList.length,
                itemBuilder: (_, index){
                  print(_taskController.myList.length);
                  return Container();
                  );
            });
          }),
        );

    You must use Obx and wrap ListView.builder using to make the list obsersavable or have state.

    Here _taskController is an instance of GetxController. 

    Getx refresh a list

    If you add things dynamically or remove or update, you might not get new value immediately without refreshing the list. 

    To get the most recent value from the list, you need to refresh the list to get updated value. You can do like below

    _taskController.myList.refresh();

    If you are updating list from the database, then you need to do query first, then replace the old list value with assignAll() function.

    void getTasks(){
        List<Map<String, dynamic>> lists= await db.query();
        myList.assignAll(lists.map((e)=>Model.fromJson(e)).toList();
    }

    You need to modify the above code based on your needs. But to replace the old values you must use assignAll() funciton.

    Also do remember that, if you use Get.back() to go another page and expect the new value in the list, you must call the above code before you use Get.back() to go back to another page.

    You can do like this

    getTasks();
    Get.back();

    It will make sure, you get the most recenlty updated value in Getx List Flutter.

    Getx routes

    There are a few helper method in Getx for route management. They are 

    Get.to()       Get.toNamed()        Get.back()       and GetPage()

    In general you can use Get.to () for going to a new page. It takes the page name inside. That's as easy as that.

    Get.to(()=>DetailPage());

    Now you will go to DetailPage. The file name could be anything. It's the beaty about Getx. It will find the corresponding file and will navigate you there.

    So you would wrap it inside onPressed or onTap event.

    onPressed:(){
      Get.to(()=>DetailPage());
    }

     

    You can also specify routes directly inside GetMaterialApp(). All you need to do is to mention initialRoutes and getPages

    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return GetMaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
    
            primarySwatch: Colors.blue,
          ),
            initialRoute: "/",
            getPages: [
              GetPage(name: "/", page: ()=>MyHomePage()),
              GetPage(name: "/detail", page: ()=>DetailPage())
            ]
    
        );
      }
    }
    

    initialRoutes refer to the homepage. So do this with a slash "/" .

    But you can change to any dart file to the home page. 

    Additional routes you can mention within getPages. It takes a list of routes. If you mention routes in getPages list, you can refer them later in your other pages like below

    Get.toNamed("/detail/");

    You can also use named route from onTap or onPressed().

    The beautiful thing about named route is that, you can change the page name any time in your application. But you don't need to change the route name.

    For big projrects It would save your time.

    Passing Arguments

    If you use flutter getx it's very easy to send arguments. For passing arguments you can do like below

    Get.toNamed('/detail/', arguments:{
      
     });

    The field arguments take a dart map

    Since it takes a map, you need to send arguments like key pair value. So you can do like below

    Get.toNamed('/detail/', arguments:{
           "title":"Page title"
     });

    So in detail page you can grab the key which is "title" in our case. So in detail page will do like this to grab the argument value

    Get.arguments['title']

    If you want to go back to previous page, from your onTap() or onPresses() you can simply call

    Get.back()

    Getx pass screen or widget as arguments

    Getx Controllers Deleted

    In general when you load the dependencies, if you mark your controller as fenix:true, your controllers should not get deleted. See it like below

      Get.lazyPut(() => ProductController(productRepo: Get.find()), fenix: true);
     

    You can put here any controllers you want. 

    But there are times getx controllers still get deleted. That happens if you use Get.offNamed(your_route) to go or navigate to a different screen. 

    Instead of using Get.offNamed(), you may try to use Get.toNamed () or Get.to().

    But if you really have to use Get.offNamed(), then you could wrap your GetMaterialApp() inside GetBuilder() like below

          return  GetBuilder<ProductController>(builder:(_){
              return GetBuilder<PopularProduct>(builder: (_){
                return GetMaterialApp(
                  scrollBehavior: AppScrollBehavior(),
                  debugShowCheckedModeBanner: false,
                  title: 'Flutter Demo',
                  theme: ThemeData(
                    primaryColor: AppColors.mainColor,
                    fontFamily: "Lato",
                  ),
    
                  initialRoute: RouteHelper.getSplashRoute(),
                  getPages: RouteHelper.routes,
                  defaultTransition: Transition.topLevel,
                );
              });
            });

     

    Try to use your GetBuilder() in the build method of the main function of your app. And then wrap GetMaterialApp using GetBuilder(). But of course, you need to inject controllers inside <> this. Like we did in the above example 

    GetBuilder<ProductController>(builder:(_)){.....}

    Your problem should be solved.

    Getx GetView

    GetView makes it easy to use controller instances without creating or find the controller using below method.

    Get.find<YourController>()

    See the code below to get the controller and replace StatelessWidget 

    class MyMenuScreen extends GetView<MyZoomDrawerController> {
      const MyMenuScreen({Key? key}) : super(key: key);

    and in yor view you just need to write controller keyword and access the properties of the controller.

    BackButton(
                    color: Colors.white,
                    onPressed: (){
                      controller.toogleDrawer();
                    },
                  )

    See that, in the BackButton() Widget we are using controller instance to access the method toogleDrawer(). So GetView helps you reduce of your code and you dont need to keep track of your controller all the time.

    Getx GetBuilder With ID

    We will see how to update a certain widget in flutter using Getx GetBuilder.

    Your certain controller could be used in many different places. And the same controller could be found in many different places.

    If you use GetBuilder for that controller in the UI, then when you trigger update using the update() method, controller would update the state in every Ui, that is using the certain controller.

    This operation is quite expensive and unnecessary. You may use ID with GetBuilder and the same ID should be mentioned in the update() method. Because update() takes a list of IDs as a string.

    Getx Make Model Obs

    Here we will see how to make a model obs type or create a custom object and make it observable. It's easy to do. First make sure you have your class or model ready. In this case I will use a UserItem model. Let's take a look

    class UserItem {
      String? name;
      String? description;
    
      UserItem({
    
        this.name,
        this.description,
    
      });
    }

    Now, we will create a controller and in the controller we will make it obs type.

    class UserController extends GetxController{
      var _profile = UserItem().obs;
    
    }

    Here we will also create a getter to get the _profile variable from the UI. Now here _profile is Rx type. We will create a new UserItem object in onInit() method and change value in onReady() method.

      UserItem get profile => _profile.value;
    
      @override
      void onInit(){
        super.onInit();
        var profile = UserItem(
          name: "ahmed",
          description: "He is a freelancer"
        );
        _profile(profile);
      }

    If you run the app now, you will see name and description on the UI. This is our first successful example how to make a model or object obs type.

    After that we also wanna change value from UI. That's why we will do this in our onReady() method.

      @override
      void onReady(){
        super.onReady();
        Future.delayed(Duration(seconds: 2), (){
          _profile.value.name="dbestech";
          print(_profile.value.name);
          _profile.refresh();
        });
      }

    This method triggers a change after 2 seconds later and update the name value. And also make sure _profile gets the updated value, so we will refresh() function.

    Let's see the complete code

    class UserController extends GetxController{
      var _profile = UserItem().obs;
      UserItem get profile => _profile.value;
    
      @override
      void onInit(){
        super.onInit();
        var profile = UserItem(
          name: "ahmed",
          description: "He is a freelancer"
        );
        _profile(profile);
      }
    
      @override
      void onReady(){
        super.onReady();
        Future.delayed(Duration(seconds: 2), (){
          _profile.value.name="dbestech";
          print(_profile.value.name);
          _profile.refresh();
        });
      }
    }

    So onInit() would show the value of the name at first place, and two seconds later, onReady() method would show the udpated value.

    Let's take a look at the UI.

    void main() {
      Get.put(UserController());
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.pink,
          ),
          home: const HomePage(),
        );
      }
    }
    
    class HomePage extends GetView<UserController> {
      const HomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Center(
          child: Obx(
              ()=>Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    "${controller.profile.name}",
                    style: TextStyle(fontSize: 25),
                  ),
                  Text(
                    "${controller.profile.description}",
                    style: TextStyle(fontSize: 25),
                  )
                ],
              )
          ),
        ));
      }
    }

    Recent posts