State Management in Flutter
Josip K.
2021-02-11
7min
Development
You’ve been delaying it, but It’s time to tackle this one. Learn about State Management with simple-to-understand examples and elevate your Flutter skills.
Intro
State Management is a hot topic among developers working on all kinds of platforms, frameworks or stacks.
In this article I will explain more about State in Flutter applications. I will explain the reasons for using a State Management library and provide simple examples to get you started.
What is State?
There are various interpretations of State, but I will try to keep it as simple and concise as possible. When a developer refers to the State of their application, they are referring to the behaviour of their application at any given moment in time. Alternative interpretation would be that State is simply the data that has the ability to change within your app.
Now, you would have a very boring application if the data would never change. The app needs to be reactive; data should change constantly. Those changes in data will affect the UI that the user sees and user’s interactions have a direct correlation to the changes in the aforementioned data.
It all works together and forms a symbiotic relationship.
Why do we need to manage State?
Let’s say you’re creating a simple task management app, but before the user can access it, they need to enter their username and password. It’s not too difficult to pass those two values to the method that handles authentication.
The user is signed in and they’re greeted with an image of a lazy cat. They currently have no tasks so we will obviously use some humorous image to fill up the emptiness.
But the cat will not stay lazy for long. User can add new tasks, check them off, edit them or delete them altogether. Let’s say you decided to add tags functionality so the tasks can be grouped together.
The app is getting more complex and problems occur when data needs to be passed from one widget to the other.
There are lots of places to change data when you think about it. And we’re discussing a simple task management application.
In this scenario, State Management comes on the scene. State Management is the process of handling these data changes and moving information into and out of the Flutter widget tree.
State Management in Flutter
In Flutter, you can store your data in the main Widget and easily send data to child Widgets. Troubles occur if you want to send some data from a child Widget to the parent Widget. And if we check the task management application I mentioned above, that scenario is very likely to happen in a dozen of places.
As you can see, moving data from children to the upper portions of the Widget tree is a bit trickier. That’s where Flutter State Management libraries come into play. You can include them in your pubspec.yaml file as dependencies and start using them like any other package.
Pub.dev boasts diverse packages ranging from simple ones which solve simple use cases to more robust ones that offer simpler syntax and additional functionality. Today, I’m going to show you how to work with GetX.
Using GetX
So let’s start using GetX for our State Management needs. I will go through a simple example, but this should be more than enough for you to start using the package and implementing it in your application.
GetX offers a couple of methods to consume your data and to manipulate it wherever you are in the Widget tree. One of them is done by using the Obx() Widget, and I plan on showing you the procedure of using it in your application.
Let’s jump into coding!
1. Add GetX to your
pubspec.yaml
fileBefore starting to write code, we must add
GetX
to our pubspec.yaml
file.
GetX is often updated so watch out for any breaking changes. They shouldn’t happen, but always be vigilant for those.You can eliminate problems by specifying the exact version you want, but I will omit it from our pubspec.yaml file today.
name: getx_tutorial description: A new Flutter project. publish_to: 'none' version: 1.0.0+1 environment: sdk: '>=2.7.0 <3.0.0' dependencies: flutter: sdk: flutter cupertino_icons: ^1.0.0 get: dev_dependencies: flutter_test: sdk: flutter flutter: uses-material-design: true assets: - images/
2. Create your controller file
The controller file will contain all your business logic. Relevant variables, methods and everything that should be hidden away from the UI section.
In this article, I extended my class with GetxController and inside it I created a simple Car model. The model contains the maker, model, color and top speed of the car.
I plan on fetching the data by simulating a network call in a dedicated method. When the data arrives from an external location, it will contain three Car instances.
import 'package:get/get.dart'; import '../models/car.dart'; class CarsController extends GetxController { // By using 'obs', this is an observable variable var carList = List<Car>().obs; @override void onInit() { super.onInit(); // When class gets instanced, fetch cars from an API fetchCars(); } void fetchCars() async { // Simulates a network call await Future.delayed(Duration(seconds: 3)); var response = [ Car(...), Car(...), Car(...), ]; // Assign fetched values to your carList carList.assignAll(response); } }
As you can see, by overriding the onInit() method, I triggered the method that gets the response from a simulated network call. After that, I assigned the values to the previously declared carList variable.
A very important thing to notice is the way I declared the above mentioned variable. Adding .obs to it will make it reactive and capable of using Obx().
3. Create the screen file
The final coding episode in this article will be writing the logic that handles the UI. Let’s create Widgets that will elegantly show our newly fetched car data and see how to include the
CarsController
class.
@override Widget build(BuildContext context) { // Initialize the dependency here with Get.put final CarsController _carsController = Get.put(CarsController()); return Scaffold( body: Obx( () => ListView.builder( itemCount: _carsController.carList.length, itemBuilder: (BuildContext context, int index) { final Car car = _carsController.carList[index]; return Text( '${car.maker} ${car.model} is a beautiful ${car.color} car with a top speed of ${car.topSpeed}.'); }, ), ), ); }
The first unorthodox thing you’ll notice is the initialization of our previously created CarsController. Using simple syntax like Get.put(), we can do the initialization and proceed further.
I created a ListView.builder which renders a Widget I created to display the fetched list of cars. The interesting part is how we get the data from the car list. I simply call the class I initialized before and after the dot, I can use the data contained within it.
Since I initialized the CarsController, it triggered that initState method I declared earlier. It fetched the data and added it to the cars list.
More about GetX
This is just the tip of the iceberg. GetX is incredibly versatile and offers much more than State Management.
More intuitive Route Management, not needing the context virtually anywhere, triggering dialogs, modals or snackbars very trivially… There’s a lot more that will make developing Flutter applications a much more pleasant adventure.
I won’t dive into it here but I encourage you to check the official documentation and start tinkering with it.
In conclusion
There's no best state management solution in Flutter, each has its benefits and drawbacks. Experience, judge for yourself and see what fits your project requirements.
This is just the start, you can see much more by reading the documentation on each package. I encourage you to dig deeper and recognise the great potential these packages offer.
The one we discussed in this article, GetX, is easy to use, robust and has additional features that make creating Flutter apps an even bigger joy.
I hope it will bring you even closer to Flutter and put a smile on your face while you’re developing your next idea.
Subscribe to our newsletter
We send bi-weekly blogs on design, technology and business topics.