Getting Started With The GetX Package In Flutter Applications
Flutter is one of the fastest ways to build truly cross-platform native applications. It provides features allowing the developer to build a truly beautiful UI experience for their users.
However, most times to achieve things like navigating to screens, state management, and show alerts, a lot of boilerplates are needed. These boilerplates tend to slow down the development efficiency of developers trying to go about building features and meeting their deadlines.
Take for example the boilerplate needed to navigate to a screen in a Flutter application. Let’s say you want to navigate to a screen called AboutScreen
. you will have to write:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => AboutScreen()),
);
It would be more efficient and developer-friendly to do something like:
Get.to(AboutScreen());
When you need to navigate back to the previous page in Flutter you will have to write:
Navigator.pop(context);
You will notice we are always depending on context property for something as commonplace as navigating between screens. What if instead, we can do something like this:
Get.back();
The above examples are some of the ways where application development in Flutter can be improved to be more intuitive and efficient with less boilerplate. If you favor simplicity and being efficient in building out features and ideas, in Flutter then the Get package will interest you.
What Is GetX
Get or GetX is a fast, stable, extra-light framework for building Flutter applications.
GetX ships out of the box with high-performance state management, intelligent dependency injection, and route management in a simplistic and practical way.
GetX aims to minimize boilerplates while also providing simple and intuitive syntax for developers to use while building their applications. At the core of GetX are these 3 principles:
- Performance
GetX focuses on the performance of your application by implementing its features to consume as little resources as possible. - Productivity
GetX wants developers to use its features to be productive as quickly as possible. It does so by employing easy to remember syntax and practices. For example, generally, the developer should be concerned to remove controllers from memory but GetX out of the box provides smart management that monitors controllers in your application and remove them when they are not being used by default. - Organization
GetX allows the decoupling of the View, presentation logic, business logic, dependency injection, and navigation in your Flutter application. You do not need context to navigate between routes, so you are not dependent on the widget tree for navigation. You don’t need context to access your controllers/blocs through aninheritedWidget
, so you can completely decouple your presentation logic and business logic from your view layer. You do not need to inject your Controllers/Models/Blocs classes into your widget tree through multiproviders, for this GetX uses its own dependency injection feature, decoupling the DI from its view completely.
Features Of GetX
GetX comes with a couple of features you will need in your daily app development in Flutter. Let’s look at them:
State Management
One of the flagship features of GetX is its intuitive state management feature. State management in GetX can be achieved with little or no boilerplate.
Route Management
GetX provides API for navigating within the Flutter application. This API is simple and with less code needed.
Dependency Management
GetX provides a smart way to manage dependencies in your Flutter application like the view controllers. GetX will remove any controller not being used at the moment from memory. This was a task you as the developer will have to do manually but GetX does that for you automatically out of the box.
Internationalization
GetX provides i18n out of the box allowing you to write applications with various language support.
Validation
GetX provides validation methods for performing input validation in your Flutter applications. This is quite convenient as you wouldn’t need to install a separate validation package.
Storage
GetX provides a fast, extra light, and synchronous key-value in memory, which backs up data to disk at each operation. It is written entirely in Dart and easily integrates with the core GetX package.
Getting Started With GetX
Now that you have seen what GetX is and the features and benefits it provides, let’s see how to set it up in your application. We will build a demo app to see most of the features we have mentioned in action. Let’s get started.
Create A Brand New Flutter Application
We will get started by creating a brand new Flutter application through the Flutter CLI. I am assuming your machine is already set up for application development with Flutter. So we run:
flutter create getx_demo
This will generate the basic code needed for a Flutter application. Next, open up the project you just created in your editor of choice (We will be using VS Code for this article). We will then run the project to make sure it’s working alright (Make sure you have either a device connected or an emulator/simulator running).
When the application runs, you will see the default counter application that Flutter scaffold for you when you create a new Flutter application. What we are going to do is to implement the very same counter application but with GetX to manage the state of the app (which is the count variable).
We will start with clearing main.dart
and leaving only this snippet of code:
# main.dart
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
By now our application would have been broken since there is no MyHomePage
widget anymore. Let’s fix that. With GetX, we don’t need stateful widgets and also our UI can be clearly separated from our business logic. So we will create two directories inside lib/
. These directories are:
views/ |
To hold the screens in our application. |
controllers/ |
To hold all controllers for the screens in our application. |
Let’s create MyHomePage
widget inside views/
. The name of the file will be my_home_page.dart
. After you create it, add the following code snippet to it:
import 'package:flutter/material.dart';
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'0',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: null,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Now we have the MyHomePage
widget, let’s import it in main.dart
. Add the import statement to the top of main.dart below import 'package:flutter/material.dart';
import './views/my_home_page.dart';
Now your main.dart
file should look like this:
import 'package:flutter/material.dart';
import './views/my_home_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
When you save your application now, all errors should have been fixed and the app will run. But you will notice when you click the button again, the counter won’t be updated. If you look at the views/my_home_page.dart
code, you will see we are just hard coding 0
as the value of the Text widget and passing null
to the onPressed
handler of the button. Let’s bring in GetX to the mix to get the application functional again.
Installing GetX
Head over to the install page for GetX on pub.dev and you will see the line of code to copy to place in your pubspec.yml
file to install GetX. As of the time of writing this article, the current version of GetX is 3.23.1. So we will copy the line:
get: ^3.23.1
And then paste it under the dependencies
section of our pubspec.yml
file. When you save the file, get should be automatically installed for you. Or you can run manually in your terminal.
flutter pub get
The dependencies section of your pubspec.yml
file should look like this:
dependencies:
flutter:
sdk: flutter
get: ^3.23.1
GetxController
We have mentioned that GetX allows you to separate the UI of your application from the logic. It does this by providing a GetxController
class which you can inherit to create controller classes for the views of your application. For our current app, we have one view so we will create a controller for that view. Head over to the controllers/
directory and create a file called my_home_page_controller.dart
. This will hold the controller for the MyHomePage
view.
After you’ve created the file, first import the GetX package by adding this to the top of the file:
import 'package:get/get.dart';
Then you will create a class called MyHomePageController
inside it and extend the GetxController
class. This is how the file should look like:
import 'package:get/get.dart';
class MyHomePageController extends GetxController {}
let’s add the count state to the class we’ve created.
final count = 0;
In GetX, to make a variable observable — this means that when it changes, other parts of our application depending on it will be notified. To do this we simply need to add .obs
to the variable initialization. So for our above count
variable, we will add .obs
to 0
. So the above declaration will now look like this:
final count = 0.obs;
This is how our controller file looks like at the moment:
import 'package:get/get.dart';
class MyHomePageController extends GetxController {
final count = 0.obs;
}
To wrap things up with the MyHomePageController
we will implement the increment
method. This is the snippet to do that:
increment() => count.value++;
You will notice we needed to add .value
to the count variable to increment it. We did this because adding .obs
to a variable makes it an observable variable and to get the value of an observable variable, you do so from the value
property.
So we are done with the controller. Now when the value of count changes, any part of our application using it will be updated automatically.
We will now head over to our view and let it know about the controller we just created. We will do so by instantiating the controller class using GetX dependency management feature. This will ensure that our controller won’t be in memory when it is no longer needed.
In views/my_home_page.dart
import the Get package and also the controller you created like so:
import 'package:get/get.dart';
import '../controllers/my_home_page_controller.dart';
Then inside the MyHomePage
class we will instantiate the MyHomePageController
:
final MyHomePageController controller = Get.put(MyHomePageController());
Now we have an instance of the MyHomePageController
, we can use the state variable as well as the method. So starting with the state, in GetX to mark a part of your UI to be rebuilt when a state variable changes, you will wrap that part with the Obx
widget. GetX provides other ways of doing this, but this method is much simpler and cleaner.
For our count application we want the Text widget to be updated with the current count. So we will wrap the Text widget with Obx
widget like so:
Obx(() => Text('0',style: Theme.of(context).textTheme.headline4,),)
Next, we will replace the static string 0
with the count variable from the MyHomePageController
like so:
Obx(() => Text('${controller.count.value}',
,style: Theme.of(context).textTheme.headline4,),)
Lastly, we will call the increment method when the floatingActionButton
is pressed like so:
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
So overall, our MyHomePage
view file should now look like this:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/my_home_page_controller.dart';
class MyHomePage extends StatelessWidget {
final String title;
final MyHomePageController controller = Get.put(MyHomePageController());
MyHomePage({this.title});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Obx(
() => Text(
'${controller.count.value}',
style: Theme.of(context).textTheme.headline4,
),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
When you save your application or you rerun it, the counter app should be working as it did when we first created the application.
I believe you have seen how intuitive state management is with GetX, we didn’t have to write a lot of boilerplate and this simplicity will be more obvious as your application get complex. You will also notice our view doesn’t hold or maintain any state so it can be a stateless widget. The brain of the view in turn is now a controller class that will hold the state for the view and methods.
Navigation In GetX
We have seen state management in GetX. Let’s now look at how GetX supports Navigation within your application. To activate the Navigation feature of GetX, you only need to make one change in main.dart
which is to turn the MaterialApp
widget to a GetMaterialApp
widget. Let’s do that by first importing Get in the top of main.dart
import 'package:get/get.dart';
Then we make the change to MaterialApp
so our main.dart
file now look like this:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './views/my_home_page.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
Now our app has been set up to support GetX navigation. To test this out we will create another view in views/
directory. We will call this on about_page.dart
and it will contain the following code:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../controllers/my_home_page_controller.dart';
class AboutPage extends StatelessWidget {
final MyHomePageController controller = Get.put(MyHomePageController());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('About GetX'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'GetX is an extra-light and powerful solution for Flutter. It combines high performance state management, intelligent dependency injection, and route management in a quick and practical way.',
),
),
],
),
),
);
}
}
We will then go over to MyHomePage
and add a button that when pressed will navigate us to the AboutPage
. Like so. The button should be below the Obx widget. Here it is:
FlatButton(onPressed: () {}, child: Text('About GetX'))
We will also need to import the AboutPage
on top of the MyHomePage
file:
import './about_page.dart';
To tell GetX to navigate to the AboutPage
all we need is one line of code which is:
Get.to(AboutPage());
Let’s add that to the onPressed
callback of the FlatButton
widget like so:
FlatButton(
onPressed: () {
Get.to(AboutPage());
},
child: Text('About GetX'))
When you save your application now, you will now be able to navigate to the AboutPage
.
You can also choose to replace the MyHomePage
view with the AboutPage
so the user won’t be able to navigate back to the previous page by hitting the device back button. This is useful for screens like login screens. To do this, replace the content of the onPressed
handler with the below code:
Get.off(AboutPage());
This will pop the MyHomePage
view and replace it with AboutPage
.
Now that we can navigate to the AboutPage
, I think it won’t be so bad to be able to go back to MyHomePage
to do this we will add a button in AboutPage
after the Padding widget and in it’s onPressed
handler we will make a call to Get.back()
to navigate back to the MyHomePage
:
FlatButton(
onPressed: () {
Get.back();
},
child: Text('Go Home')
)
Snackbar
In Flutter conventionally to show a Snackbar, you will need to write something like this:
final snackBar = SnackBar(content: Text('Yay! A SnackBar!'));
// Find the Scaffold in the widget tree and use it to show a SnackBar.
Scaffold.of(context).showSnackBar(snackBar);
You can observe we are still depending on the context
property. Let’s see how we can achieve this in GetX. Go into the MyHomePage
view and add another FlatButton
widget below the last button we added. Here is the snippet for the button:
FlatButton(
onPressed: () {
// TODO: Implement Snackbar
},
child: Text('Show Snackbar'))
Let’s display the message ‘Yay! Awesome GetX Snackbar’. Inside the onPressed handler function add the below line of code:
Get.snackbar('GetX Snackbar', 'Yay! Awesome GetX Snackbar');
Run your application and when you click on the “Show Snackbar button” you will see a snackbar on top of your application!
See how we reduced the number of lines needed to show a snackbar in a Flutter application? Let’s do some more customization on the Snackbar; Let’s make it appear at the bottom of the app. Change the code to this:
Get.snackbar('GetX Snackbar', 'Yay! Awesome GetX Snackbar',snackPosition:SnackPosition.BOTTOM,
);
Save and run your application and the Snackbar will now appear at the bottom of the application. How about we change the background color of the Snackbar as it is at the moment transparent. We will change it to an amberAccent
color from the Colors
class in Flutter. Update the code to this:
Get.snackbar('GetX Snackbar', 'Yay! Awesome GetX Snackbar',snackPosition:SnackPosition.BOTTOM, backgroundColor: Colors.amberAccent
);
Overall, the button code should look like this:
FlatButton(
onPressed: () {
Get.snackbar('GetX Snackbar', 'Yay! Awesome GetX Snackbar',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.amberAccent);
},
child: Text('Show Snackbar'))
Dialog
GetX provides a simple method for creating AlertDialog in Flutter. Let’s see it in action. Create another button below the previous one:
FlatButton(
onPressed: () {
// TODO: Show alert dialog
},
child: Text('Show AlertDialog'))
Let’s call GetX to display an alert Dialog:
Get.defaultDialog();
That will show a default Alert Dialog that is dismissable by tapping outside the Dialog. You can see how in one line of code we have a working alert dialog. Let’s customize it a bit. Let’s change the title and the message:
Get.defaultDialog(
title: 'GetX Alert', middleText: 'Simple GetX alert');
Save and run your app and you will see the changes when you hit the “Show AlertDialog” button. We can add confirm and Cancel buttons like so:
Get.defaultDialog(
title: 'GetX Alert',
middleText: 'Simple GetX alert',
textConfirm: 'Okay',
confirmTextColor: Colors.amberAccent,
textCancel: 'Cancel');
There are a lot of ways to customize the GetX dialog and the API is quite intuitive and simple.
Conclusion
GetX was created to improve the productivity of Flutter developers as they build out features. Instead of having to search for boilerplate needed to do things like state management, navigation management, and more, GetX provides a simple intuitive API to achieve these activities without sacrificing performance. This article introduces you to GetX and how to get started using it in your Flutter applications.
- You can find the demo here →