What’s Getx?

Amirsalar Salehi
10 min readJan 14, 2023

--

Getx is a stable and powerful library for Flutter that can we use for

  • State Management
  • Route Management
  • Dependency Injection

How to install

Add Get to your pubspec.yaml file

dependencies:
get:

Import get.dart in files that will be used

import 'package:get/get.dart';

in main.dart, use GetMaterialApp instead of having MaterialApp.

void main() => runApp(GetMaterialApp(home: HomeView()));

Why we should use Getx?

Getx focused on Performance, Code-Organization, and Plain Syntax.

Let's explain each one.

Performance

Getx emphasized performance and clean architecture and compared to other libraries like Bloc, Provider, etc, in my opinion, and analysis, Getx is on top. in the research, Getx used less memory than other libraries

State Management RAM Usage Chart (Source: bacancytechnology)

Code-Organization

Getx has decoupled View, Controller, Dependency Injection, and Navigation layers. It means that the code you write is more readable and everything is on its layer.

Code-Organization Sample

It’s just an image to describe the organization, don’t be afraid, I’ll explain later what they are and why we use them 😀.

Plain Syntax

Getx has a straightforward syntax. Just like above, to get a repository from dependency injection you just write one line code.

Get.find<HomeRepository>();

Getx Features

Let’s talk about some features that Getx gives us.

State Management

First of all, let’s talk about what is State in Flutter is. In every view, we have some widgets that will be updated over time. So the information that changes every time and needs to be kept and updated in UI, we call it State.

Getx provides two types of State Management

  • Simple State Management
  • Reactive State Management

So what they are? Stay with me and see

Simple State Management

Simple State Management Sample With GetBuilder

In this example, we have a simple string variable in our controller. Whenever the changeText() function is called, we want to update our widget that is listening to the string variable by using GetBuilder. If we don’t call the update() function, the widget won’t be updated because the GetBuilder widget waits for it and when we don’t call it, it won’t work though.

Update the Specific GetBuilder widget

In addition to builder callback, GetBuilder also has an id argument. If you want to update the widget with a unique id, you pass the id through the update() function and it only updates the GetBuilder widget with given the id(note that update() function takes a List of Ids). I’ll show you an example.

Update GetBuilder with a unique Id

In our view, we have two widgets with different Ids and in the controller, we have a function that updates the text’s value but we only update one of them in the widget state. In the example, we call the update() function with a List of Ids that is the only id of the GetBuilder named ‘first’ that will be updated in the UI.

Reactive State Management

Reactive State Management Sample With Obx

So what did we do in the above sample? First of all, we have a view called HomeView that extends StatelessWidget(To clean code and best practice, use Getx class to build views. Like extends GetView or GetResponsiveView I’’ be explain later). In the body argument, we got the Obx widget. What does it use for? So in the Obx callback, we must return a widget that used an Rx variable(if you won’t, you’ll get an exception). e.g. we just want to display the Rx bool value in the Text widget. Initially return your Text widget in Obx callback and boom, That’s it. Whenever you change your value, Widget Automatically gets updated, and don’t need to call any function to update the UI like what we do in Simple State Management.

What is the difference between GetView and GetResponsiveView?

That’s obvious. You can guess it by looking at their names. The difference is, in GetResponsiveView you have access to a variable called screen that gives you a ResponsiveScreen instance and you can manage your design per device.

Dependency Injection

Getx has a simple dependency injection. With single-line code, you can inject dependency and on the opposite, you can get the dependency as well. The injection can be done by different methods, I’ll explain two of them:

Get.put<DataType>(value) 
Get.lazyPut<DataType>(() => value);

What is their difference? (Official Document)

  • Get.put: Injects an Instance<DataType> in memory
  • Get.lazyPut: Create a new Instance<DataType> lazily from the builder callback. The first time you call Get.find, the builder callback will create the Instance and persist as a Singleton (like you would use Get.put).

If you are using Get’s State Manager, pay more attention to the bindings API, which will make it easier to connect your view to your controller.

Bindings

In Getx we have a Bindings class for each view. What does it do? so whenever you created a binding class, all the dependencies that you inject, All services that you want to start when View is created, or even your controller, will bind to your view. What’s that mean? It means that they will create and destroy depending on your view’s lifecycle.

Binding Class

thus you create your binding class, let’s bind it to the preferred view.

you can attach your binding class in different ways

Binding in Named Routes

Just pass the binding class to GetPage’s binding argument

GetPage(
name: "/",
page: () => HomeView(),
binding: HomeBinding(),
)

if you don’t want to create a binding class, you can use BindingsBuilder callback like below

GetPage(
name: '/',
page: () => HomeView(),
binding: BindingsBuilder(() {
Get.lazyPut(() => HomeViewRepository());
Get.lazyPut(() => HomeViewController(repository: Get.find<HomeViewRepository>()));
}),
),

Binding in Normal Routes

Just Pass The binding class or BindingsBuilder callback through the Get.to function

Get.to(HomeView(), binding: HomeBinding());

Route Management

if you want to get rid of context for navigation to a specific route or show some dialog, snackbar, toast, etc, Getx is the best thing you choose.

Navigate To The Unnamed Routes

To navigate to a new screen

Get.to(HomeView());

To close everything you’ve opened like the screen, snackbar, dialog, etc

Get.back();

To navigate to a new screen and have no option to return (Splash or Auth screen)

Get.off(HomeView());

To navigate to a new screen and cancel all previous routes(e.g. Splash -> Login -> HomeView. all previous routes will be deleted from the stack)

Get.offAll(HomeView());

To navigate to the new screen and do something base on what you return as a result

var result = await Get.to(ProductView());

on the new screen, send back the result data

Get.back(result: ResultEnum.AddToCart);

To navigate to the new screen and send an argument (arguments take a dynamic value)

Get.to(HomeView(), arguments: true);

on the next screen, you can get arguments and do some functionality base on your arguments

var args = Get.arguments;

Navigation To The Named Route

First of all, To create a named route, for each screen you want to have, you must create a List of GetPage class. each screen mu have a GetPage instance and assign it to the getPages parameter into GetMaterialApp widget that you create in main.dart.

runApp(
GetMaterialApp(
initialRoute: '/splash',
unknownRoute: GetPage(name: '/404', page: () => NotFoundView()),
getPages: [
GetPage(name: '/splash', page: () => SplashView()),
GetPage(name: '/', page: () => HomeView()),
GetPage(name: '/product', page: () => ProductView()),
],
)
);

initialRoute: defines the first page of the application

unknownRoute: To handle non-defined routes (404 NotFound View)

getPages: defines the pages and screens that our application has.

What is GetPage

GetPage is a class that defines a screen for each route we are navigating.

Principal GetPage’s arguments

name: defines a route for the page

page: a callback method for initializing the screen view

binding: attach the binding class to the page

bindings: attach a list of binding classes to the page

middlewares: attach a list of GetMiddleware to the page

Navigation

To navigate and send data to a new screen

Get.toNamed('/product', argument: 136);

on the new screen, you can get the arguments

var args = Get.arguments

also, we have offNamed, offAllNamed, etc, like Unnamed Routes.

Dynamic URLs

Getx has handled the Dynamic URLs in their packages. You can pass the parameters through the routes and use them on another screen.

Get.offNamed('/product?id=1381');

on another screen

var id = Get.parameters['id'];

Named Parameters

In GetPage you can define Named Parameters and use it as below

GetPage(
name: '/product/',
page: () => ProductView()
),
GetPage(
name: '/product/:id',
page: () => ProductView()
),

In the above example, first, we define the page without named parameters(note that you must set the / at the end of the route), and in the second object, we defined it.

Passing data in Named parameters

Get.offNamed('/product/1381');

on the other screen

var id = Get.parameters['id'];

Dynamic URLs And NamedParameters Mixture

You can pass the data in both ways. But if you want to mix them and use their approaches together.

Passing Data

Get.offNamed('/product/1381?flag=true');

// OR

var parameters = <String, String>{"flag": "true"};
Get.offNamed('/product/1381', parameters: parameters);

on the other screen

var id = Get.parameters['id'];
var flag = Get.parameters['flag'];

Middlewares

To protect your screens with some functionality, you can use middleware classes. First, create a class that extends GetMiddleware and override the redirect method and implement the functionality

class AuthMiddleWare extends GetMiddleware {
@override
RouteSettings? redirect(String? route) {
if (LocalStorage.userInfo() == null) {
return RouteSettings(name: '/auth');
}
return null;
}
}

In the above example, we declare that if the user is not logged in and there is no information about them in Local Storage, then, we redirect them to the auth screen.

This is how you can set middleware to pages

GetPage(
name: '/product',
page: () => ProductView(),
middlewares: [AuthMiddleWare()],
),

each page taking a list of middleware in order to separate middleware.

SnackBar

Showing the snackbar, dialog, bottomsheet is so easy with Getx.

You can show it anywhere without any context with a single-line code

Get.snackbar(
'Title',
'Message',
);

Dialog

You just pass the AlertDialog widget through the Getx

Get.dialog(
AlertDialog(),
);

BottomSheet

You just need to pass the widget through the Getx

Get.bottomSheet(
Container(),
);

Utils

Getx also provides some utilities. For instance, Internationalization, and HTTP requests to communicate between your app and back-end.

Internationalization

Translations will be handled by a key-value map. You just need to create a class that extends Translations

class CustomTranslator extends Translations {
@override
Map<String, Map<String, String>> get keys => {
'en_US': {
'Hello': 'Hello Dear',
},
'fa_IR': {
'Hello': 'سلام عزیز',
}
};
}

and add the class to your GetMaterialApp

void main() => runApp(
GetMaterialApp(
home: HomeView(),
locale: Locale('en', 'US'),
fallbackLocale: Locale('fa', 'IR'),
translations: CustomTranslator(),
)
);

You can set a default locale to your app in order to translate a default language like en_US. specifying the fallback locale in case an invalid locale is selected. if you want to use the user’s locale you can call Get.deviceLocale.(That’s why we use the fallback locale. If the user locale is invalid in our app in case Get wll use the fallback locale)

Change Locale Manually

When we go further, we want the user to choose the locale and update it

var locale = Locale('fa', 'IR');
Get.updateLocale(locale);

How to use in Screens

Just append ‘ .tr ’ to the specified key and it’ll be translated

Text(
'Hello'.tr, // out put will be the translated text.
)

Translate with Parameters

You can pass parameters to the translation and it’ll put in the place

Map<String, Map<String, String>> get keys => {
'en_US': {
'Hello_Message': 'Hello @firstName , @lastName',
},
'es_ES': {
'Hello_Message': 'سلام @firstName , @lastName',
}
};

And pass the parameter

Text(
'Hello_Message'.trParams({
'firstName': 'Amirsalar',
'lastName': 'Salehi'
})
)

GetConnect

You can easily create a class that extends GetConnect Class and implement POST/GET/DELETE/PUT/SOCKET methods.

class RemoteProvider extends GetConnect {

Future<Response> getProduct(int id) => get('https://api.com/product?id=$id');
// OR
Future<Response> getProduct(int id) => get('https://api.com/product', query: {'id': id});

Future<Response> deleteOfferFactor(int id) => delete('https://api.com/product', query: {'id': id});

Future<Response> createProduct(dynamic data) => post('https://api.com/product', data);

Future<Response> updateProduct(dynamic data) => put('https://api.com/product', data);

GetSocket userProfile() => socket('https://api.com/users/socket');
}

Customize Configuration

You can customize your GetConnect client. you are able to add RequestModifier or ResponseModifier to listen to requests and responses and make changes to them or set a base URL or even the number of attempts and etc.

class RemoteProvider extends GetConnect {
@override
void onInit() {

httpClient.baseUrl = 'https://api.com';

// It's will attach Authorization for each request
httpClient.addRequestModifier((request) {
request.headers['Authorization'] = 'Bearer $token';
return request;
});

// (OFFICIAL DOCUMENT)
// Even if the server sends data from the country "Brazil",
// it will never be displayed to users, because you remove
// that data from the response, even before the response is delivered
httpClient.addResponseModifier<CasesModel>((request, response) {
CasesModel model = response.body;
if (model.countries.contains('Brazil')) {
model.countries.remove('Brazilll');
}
});

httpClient.addAuthenticator((request) async {
final response = await get("http://api.com/refreshToken");
final token = response.body['accessToken'];
// Set the header
request.headers['Authorization'] = "Bearer $token";
return request;
});

// addAuthenticator will be called 3 times
httpClient.maxAuthRetries = 3;
}
}
}

Conclusion

Getx is the best solution in order to use Smart StateManagement, DependencyInjection, and RouteManagement, and save time in coding. I hardly recommend you to use this library. If you want full documentation, see the Official Document.

With Respect, Amirsalar Salehi(Lorevantonio).

--

--

Amirsalar Salehi
Amirsalar Salehi

Responses (1)