Upgrading from ChangeNotifier
Using ChangeNotifier to architect your apps?
Feel like you can use something better?
Bloc? Feels too Complex?
Enter StateNotifier, a new way to architect your Provider apps. Well, technically no, but you’ll see what I mean.
The problem with ChangeNotifier,
ChangeNotifier might be the simplest and least boilerplate state management in flutter but it does have its own limitations. The ability to use immutable states is the one I would say lacks the most, ChangeNotifier also has slower time complexities such as O(N²) for notifylisteners() and O(N) for addListener(). Injecting your dependency service might be another headache.
So How is StateNotifier different?
If you have used ValueNotifier, it is not so different, In fact, It is a reimplementation of ValueNotifier outside of Flutter with some key differences. I’ll list a few but you can check out the documentation for more on pub.dev
StateNotifier is independent of flutter, It has improved time complexities for notifying and adding listeners. It exposes the ValueNotifier.value called state, which will be the key concept in this architecture.
That was much explanation, let’s dive into the code now.
We’ll be building a simple To-Do app using StateNotifier
I won’t be focusing on the UI code much to keep this tutorial short and less tedious.
So let’s get straight to adding our dependencies.
You’ll see some extra dependencies and additional keys that you might not be familiar with.
freezed is an amazing package from the creator of Provider and StateNotifier. We’ll be using freezed to generate our dataclas and State Class
The scripts key is another amazing tool, derry is a script manager for dart, all you have to do is activate derry globally and run your script using derry run <Key > and build_runner will start generating the code in this case. check out derry.
Let’s make our data class for a Todo.
This might be a bit strange from a normal class, but this is how you create classes in freezed, you create all the fields required within a factory constructor and mention the part files, add a fromJson constructor, and freezed makes sure JsonSerailizable classes are generated too.
Let’s create our State class which is exactly the same process as above.
We create named Constructors for each of the states and only declare the data in the state we are expecting data. This is the advantage of using immutable states, you only get the data when it is available and can easily control your UI rebuilds according to state changes. if you want to learn more about Immutable states and freezed check out these videos by Flutter GDE Pascal Welsch and Reso Coder
Alright, Moving on to the most important part of this puzzle our state controller class
Create your StateNotifier class, I’ll be naming mine TodoVM since it acts like a ViewModel following MVVM approach. and you’ll see the TodoVM extends Statenotifier along with the corresponding state <ToDoState> which is what gives access to watch the current state and rebuild your UI.
You’ll also see a special mixin, the LocatorMixin, this gives you access to the provider through a read<T>() property making dependency injection of a service a piece of cake.
I’ll be using simple localization using sembast to act as our external service and to persist data. you can check out the code for this here.
Let’s start adding methods to manage the state of our app.
But don’t forget to add your Providers first, and you’ll see StateNotifier uses its own kind of provider called StateNotifierProvider and it requires both the ViewModel and the State class.
we’ll start with an add method to create our todos. I first check for the current state, I then create a Todo Object and add it to the current list of todos. I then save it to local storage by called read<LocalStorage>().saveTodo(). And finally, to update our current state to include the added todo, we just assign the new list of todos to the state variable. And that’s it your state should now have the latest list of todos.
Similarly, we can add the rest of the operations.
As you can see in both toggle and delete we are assigning the new value to the state variable, this will change the state of the app and rebuild your UI.
Let’s just add one more thing to wrap up the controller part
finally, add this method to fetch the locally stored todos and initialize the state when the app starts or restarts. And now you have fully functioning state management for your todos.
Let’s wire up with the UI.
Create a Stateless Widget and in the Scaffold call the context.watch<T>() method from the Provider package on TodoState. freezed provides handy methods to map the Union Classe constructors corresponding to each state that we created. You can use either when() or map() to map the state and return a widget. That’s it! and now you have your todos available which you can pass down the tree and display however you wish to. I’m using a simple ListView.
Just add the relevant method calls in the widgets for the rest of the operations we wrote.
like this,
I’m just using some Buttons and a CheckBoxListTile to call the relevant methods, but you can spice up your UI as you wish.
After using ChangeNotifier for a very long while, shifting to StateNotifier has greatly improved the quality of my code, dependency injection, and cleaner UI code, and I would recommend anybody using ChanegNotifier the same!.
I hope you liked this article and understood the concept behind StateNotifer. I’ll try to push more upgrading articles in the future. Meanwhile, you can connect with me on Twitter and LinkedIn. Feedback is always welcome, and I’m pretty active on these platforms and always up for a chat! Also, check out my GitHub for the full project.
And leave some claps behind, they're free!
Until next time, See you!