Simple Flutter app architecture: the Provider way
You are reading about Flutter. You get excited that you can express yourself and create so much so quickly if what it promises is true. You give it a try. It's indeed a pleasure to develop and you can see your changes without a reload! You think you must create an app to give it a go.
But wait. You've only read about widgets and how to draw things on the screen. Where is your business logic going to live? Next to the UI building logic?
Flutter is quite opinionated on how to draw things on the screen but leaves how to organize state management and business logic to you. The Flutter community came up with various ways to do state management. I will focus on the most simple yet scalable way (that is officially recommended): the
There are plenty of great resources about the Provider state management. This post aims to give you an intro into the state management method as quickly as possible. It's a bit more concise than a tutorial and a bit more verbose than a cheat sheet.
To make Flutter redraw the screen you need to call
setState(). But this is not very convenient for a sizable app, especially when changes happen deep down in the widget hierarchy.
Provider offers a way for (stateful) widgets to get notified when a change happens in the view model that requires a call to
build() to redraw the UI.
Head to your
pubspec.yaml and add a dependency to the
provider package (check pub.dev for the latest version).
dependencies: provider: ^4.3.2+2 [...]
This is where your "business logic" will live. Whatever is not related to how things will be drawn on the screen, should live here.
This class will extend
ChangeNotifier. It's a kind of an Observable interface if you are familiar with the term. This allows the view model to notify the view when things have changed and a redraw is needed. This "notification" happens with the
Now that we have a "view model" we need to bind it with the "view".
ChangeNotifierProvider is the widget that initially creates the view model and provides it to whatever descendant requires to consume it. This wighet comes from the
provider package we installed in the setup phase.
This should be just above the higher widget that needs to consume the view model. In a simple screen-based app, each view has a single view model. So for each screen, you have a
ChangeNotifierProvider just above the screen widget.
This is the widget that asks to "consume" a "view model". Any widget that needs data from the model to set its state should be wrapped in a
Consumer widget. In the above view, we want the
Text widget to have the
text value found in our model.
Now we have the view bound to our view model. All we have to do is somehow call the model, which will make some "business logic" and trigger an update.
Here we didn't use a
Consumer to get on hold of a model reference. We didn't want any property of the model to draw our widget on the screen. We just wanted to call a method on our model.
Provider.of will get you that reference.
Hopefully, by now you have a grasp of the basics of the Provider pattern for managing state in a Flutter app. Happy coding!