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++;
}

In controller's init method you do initialization like data from network or loading data from JSON files. In the controller your varables could be obs or non obs type. In the above controller, our variable is obs type.

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.

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.

Recent posts