Flutter Provider & ChangeNotifier Architecture Guide
In this article, I will give you my vision of a good architecture in Flutter. For me, the complexity with Flutter is how you manage data and UI cleanly.
During the last 2 years, I have tried many Flutter architectures. I started with a vanilla architecture like every one, then I used the BLoC Pattern a lot, I also did a little of MobX and ScopedModel. But once I tried the Provider & ChangeNotifier architecture, I realise I had found the architecture I was looking for.
What’s a Provider?
A Provider is a Widget that will own data and will be available everywhere in your application with a BuildContext. It’s a place where you can « save » (for example authenticated user information) and get it in any Widget without making any other API call or anything else. (for Angular developers, you can call it a Service).
What’s a ChangeNotifier ?
ChangeNotifier is a native class from Flutter. We’ll use it to notify our View when one or more variables change in its ViewModel. It prevents to use the ugly SetState() function directly in our Views which would result in unmaintainable code.
Let’s see an architecture overview
So this this how this architecture works, globally. The arrows represent communication between widgets.
Let’s define some terms:
View and ViewModel
A view in Flutter is a Widget that contains only UI Widgets. To manage this view and give it data and functions, you will need a ViewModel. To summarize, one ViewModel manages one View (you could also called it Screen).
Repositories
Repositories are classes where you have all your functions to query data from database or API.
Models
Project’s Objets classes
Utils
Everything you need to help you with classes. Like for example DistanceUtils for distance functions, etc.
Constants
ProjectColors, ProjectTextStyle, etc.
Enums
All enums you need in your project
How does it work ?
In this diagram, we can see that Providers are linked to every widget even MaterialApp. It’s because MaterialApp rebuilds and doesn’t keep state at every Navigation move. You cannot retrieve data from a Widget that is not under the navigation stack, which means you need to put your Providers over it.
In this case we have AuthProvider over other widgets because most of the time, we need auth information in other Providers.
Then you can retrieve every Provider in every ViewModel to show data in your view.
Go to the code side 🛠
First of all, let’s see my directories organisation:
Pubspeck
In your pubspec, you only need to add one package : Provider (https://pub.dev/packages/provider#-readme-tab).
main.dart
This example shows how to make your Provider available in your whole app (with a AuthProvider under).
base_provider.dart
The base provider is here just to provide the dispose method or, in our example, to provide the AuthenticationProvider to other Providers.
base_view_model.dart
The BaseViewModel’s role is to hold all the providers the app needs in order to give it to ViewModels and the ChangeNotifiers.
example_screen_view_model.dart
In this example we see how to initialize and retrieve a value from a Provider and how to notify our view that a value has changed.
view_model_provider.dart
This Widget’s role is to provide a ViewModel to our View. We’ll see how use it next.
example_screen.dart
And now with our ViewModelProvider we can pass data and change data to our view 🎉
This is the end 🎬
I hope this little guide will help you !
You can find a project starter with this architecture here: https://github.com/jgrandchavin/flutter_provider_architecture_starter