How to Use Flutter Getx | Tips for Beginners to Advanced

Flutter GetX Package Explained

Know platform

You can use GetPlatform.isIOS to know if it’s iOS or not

You can do the similar operation GetPlatform.isANDROID and GetPltaform.isWeb

See the getx basic here

GetxService

You can use GetxService when you need to store data locally on the device in memory. For example, when you want to load token from the server and save it the device. This kind of service should extend GetxService. 

Another example would be when you want to load some data from the server or on the device you created some data and want to save it locally, then you should extend GetXService.

Another practice example would be when you have API client class (that brings data from server or post data to server), then this class should extend GetxService.

class ApiClient extends GetConnect implements GetxService {
  late String token;
  final String appBaseUrl;
  final SharedPreferences sharedPreferences;
  late Map<String, String> _mainHeaders;

  ApiClient({required this.sharedPreferences, required this.appBaseUrl}) {
    baseUrl = appBaseUrl;
    timeout = Duration(seconds: 30);
    token = sharedPreferences.getString(AppConstants.TOKEN)??"";
    _mainHeaders = {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer $token',
    };
  }

See that, this class implements GetxService and we are passing token and other important properties. We also passed base url.

Most of the time, you would use GetConnect and GetxService together.

Then at the same time, your controller constructor must pass initialize values for baseUrl and timeout property.

You can also watch the video below

 

GetConnect

GetConnect is an easy way to make requests and fetch data . Lot of time GetConnect and GetxService work together.  You can simply extend GetConnect and implements GetxService. If you want to use the http get() method of Getx, then your controller must extends GetConnect.

Because of GetConnect, you app won't crash suddenly. See the below example

class ApiClient extends GetConnect implements GetxService {
  late String token;
  final String appBaseUrl;
  final SharedPreferences sharedPreferences;
  late Map<String, String> _mainHeaders;

  ApiClient({required this.sharedPreferences, required this.appBaseUrl}) {
    baseUrl = appBaseUrl;
    timeout = Duration(seconds: 30);
    token = sharedPreferences.getString(AppConstants.TOKEN)??"";
    _mainHeaders = {
      'Content-Type': 'application/json; charset=UTF-8',
      'Authorization': 'Bearer $token',
    };
  }

We extend GetConnect, and in the constructor, we pass base url and timeout property.

GetConnect supports get/post/put/delete request.  If you have a method for getting Data name getData, then inside getData, you can use get() request

Future<Response> getData(String uri, {Map<String, dynamic> query, String contentType,
  Map<String, String> headers, Function(dynamic) decoder,
}) async {
  try {

    Response response = await get(
      uri,
      contentType: contentType,
      query: query,
      headers: headers ?? _mainHeaders,
      decoder: decoder,
    );
    response = handleResponse(response);

    return response;
  } catch (e) {
    return Response(statusCode: 1, statusText: e.toString());
  }
}

Here getData is using get() request to retrieve or fetch data from the server. Get() Request returns Response object. 

So if you wan to use Get(), Post(), Delete() and Update() method of Getx state management, your controller must extend GetConnect and GetxService.

GetConnect learn more here

 

Routing with Parameters

GetPage() in Getx is very interesting and you can do many things with it. It takes two parameters name and page

GetPage(name:"route name", ()=>page());

The first parameter name, you can make it dynamic. You can define a variable for that name like

String routeName="/anyname";
GetPage(name:routeName, ()=>page());

So next time you can just change the routeName value. It's very convenient if your project is very big.

The fuction it takes you can also do many things with it. For example, instead of fat arrow function, you can return a function like below

String routeName="/anyname";
GetPage(name:routeName, (){
   "your logic goes here"
    return Page();
});

After doing your logic you can even send parameters with it.

    GetPage(name: verification, page: () {
      List<int> _decode = base64Decode(Get.parameters['pass']);
      String _data = utf8.decode(_decode);
      return VerificationScreen(
        number: Get.parameters['number'], fromSignUp: Get.parameters['page'] == signUp, token: Get.parameters['token'], password: _data,
      );
    }),

The above function should give you better idea, what you can do with GetPage() function.

You can define all of your routes in a list

Class RouteHelper{  
//variables to match the name property of the GetPage()
......
.....
static List<GetPage> routes = [
    GetPage(name: initial, page: () => getRoute(DashboardScreen(pageIndex: 0))),
    GetPage(name: splash, page: () => SplashScreen(orderID: Get.parameters['id'] == 'null' ? null : Get.parameters['id'])),
    GetPage(name: language, page: () => ChooseLanguageScreen(fromMenu: Get.parameters['page'] == 'menu')),
    GetPage(name: onBoarding, page: () => OnBoardingScreen()),
    GetPage(name: signIn, page: () => SignInScreen(
      exitFromApp: Get.parameters['page'] == signUp || Get.parameters['page'] == splash || Get.parameters['page'] == onBoarding,
    ))
];

}

At the same time you can delcare name properties in your same route class.

class RouteHelper{
  ...............
  ...............
  static const String signIn = '/sign-in';
  static const String signUp = '/sign-up';
  .............
  ..............
}

Then from your view you can access them. For example you can access the sign up or sign in route like below

 Get.toNamed(RouteHelper.signUp)

 

Worker functions in Getx

If you have long running process, getx makes it much easier to keep track of it. For example you can use ever() function to keep track of the user authentication. 

In your getx AuthController can do like below

    _firebaseUser = Rx<User?>(auth.currentUser);
    _firebaseUser.bindStream(auth.userChanges());
    ever(_firebaseUser, _setScreen);

Here ever() function is a worker function, since we use this function, Whatever happens to _firebaseUser, then immediately _setScreen function will be called. 

ever() function is a perfect function for your auth controller.

Workers are only called when you actually change a variable (except the first time), this means that if you have a variable 3, and send a 3 again, worker will not be triggered, the state in the view (Obx / GetX) will refuse the status update, and nothing will be rebuilt, to avoid unnecessary rebuilds altogether.

Getx Get.back()

If you navigate and try to go back to previous page using Get.back(), sometimes this may cause unexpected issues if you are trying to see an updated value.

With Get.back(), you might not see the updated list, map or any other variables. 

Then instead of Get.back(), try to use Get.toNamed()

Getx Update() method with ID

Flutter Getx controller could be used for many different places with different widgets with the combination of GetBuilder.

Update() method takes a list of ID's that could be used to keep the track of the GetBuilder using the ID's.

  void selectAnswer(String? answer) {
    currentQuestion.value!.selectedAnswer = answer;
    update(['answers_list', 'answers_review_list']);
  }

See how Update() method takes a list of Strings as an ID. 

In the UI section you can do below

1. Wrap your widget using GetBuilder()

2. With GetBuilder mention the controller type

3. Give a ramdom unique ID for the GetBuilder()

 GetBuilder<QuizController>(
                            id: 'answers_review_list',
..............
.............
  )

Rxn type

In Getx we use Rxn to show that, a value could be as we use it.  If your list would be null during run time, then you should use Rxn<List<type>> like that. Rxn value could be null, in this case List<type> could be null.

It could be true for any other custom variable type.

Rxn<double> test= Rxn<double>();

Here test.value is null.

The Rxn object is an observable but with nullable type. In this case, since we will not know the location until we make the request, it’s initially set to null.

RxList type

RxList type you use to make a type obsersable. In other words, RxList is a list of objects, that should be obsersable. 

Special Get.find()

Here you will learn how to use Get.find() inside GetxService or GetxController.

Getx Service vs Controller

GetxService vs Controller, which one to use could be confusing. I explained in the video. Getx service for keeping your state and data until the app ends it's life cycle. 

GetxController is temporary state.

 

Getx onInit() vs onReady()

onInit() gets called before onReady(). Then it's upto you what kind of data you put on those two methods. In general you should put the most important data in in onInit(). Cuz your app might need them right after booting up or installing. 

Less important data should be in onReady() method. 

I would suggest to put the most important data like

  1. user token
  2. app config
  3. app version

to put in the onInit() method.

I would suggest to use onReady() for 

  1. slow server connection
  2. lazily retreive data from server
  3. navigation 
  4. async data loading

Getx logWriterCallback

logWriterCallback helps you keep track of the controller that's being created and instantiated. With this we can keep track of the controllers if they have error during creation and instantiation.  So it's all about writing log to console as you create and destroy the controllers.

Inside GetMaterialApp, you need to enable it. 

  enableLog: true,
  logWriterCallback: Logger.write,

logWriterCallback refers to a function. And we will create this function. 

class Logger {
  // Sample of abstract logging function
  static void write(String text, {bool isError = false}) {
    Future.microtask(() => print('** $text. isError: [$isError]'));
  }
}

The above function write() would get called every time, once a controller is being created and instantiated. and if there are errors then it will print out the error.

Recent posts