Managing Flutter State using Provider
An introduction to Provider, one of a plethora of state management solutions for Flutter.
Who Should Read This
This post is intended for those new to state management in Flutter. The article assumes you are familiar with Stateless and Stateful widgets and can navigate between screens. If not, check out the links below.
What is State Management
State is the data your application needs to display or do something with. Data which may change.
For relatively simple scenarios, where the application consists of a single screen, we could use Flutter’s Stateful widgets and call setState()
to rebuild the widget after we’ve modified some data or state. Remember, Flutter adopts a reactive model, rendering the user interface in response to state changes.
But consider the following scenario — a home screen which displays the currently signed-in user. That’s a two-screen application at a minimum and the home screen must somehow react to the action outcomes on another screen i.e. someone has logged in.
The above can be achieved by implementing a Publish-Subscribe pattern — where both screens are decoupled from one another and communication is achieved by one screen broadcasting a message, user sign in details, and another screen subscribing to that message. So let’s see how Provider can help.
So What Is Provider Exactly?
If you browse to the provider pub.dev package page you’ll see no mention of state management. Instead, it’s described as a wrapper around InheritedWidget and a quick search later you’ll see that InheritedWidget has something to do with propagating information down the widget tree. We’re getting closer.
In the context of state management, Provider is a widget that makes some value — like our user sign-in details— available to the widgets below it. Think of it as a convenient tool for passing state down the widget tree and rebuilding the UI when there are changes.
Concepts and Terminology
You need to become familiar with 4 concepts when working with provider.
- Model — a class you create to encapsulate your application data and optional methods for modifying that data. This is what is made available to other widgets. Think of it as our message to broadcast.
- ChangeNotifier — provides model change notifications to listeners. Your model above extends this class and it’s how we publish or broadcast messages.
- Consumer — a provider package widget which reacts to
ChangeNotifier
changes and calls build method to apply model updates. This is our subscriber. - ChangeNotifierProvider — a provider package widget that provides an instance of a
ChangeNotifier
to its descendant widgets.Consumer
widgets need to know theChangeNotifier
instance they need to observe. That’s the role ofChangeNotifierProvider
.
A Provider Sign-In App
We’re going to build the 2 screen app outlined earlier to show Provider in action.
▹ Create a new Flutter app in VS Code or using the Flutter CLI:
flutter create provider_login_app
▹ Next, we need to add the provider package as a dependency. To do this, add an entry for provider to the pubspec.yaml file.
▹ Install the provider package using the Flutter CLI pub get command:
flutter pub get
▹ You can import provider in your code using:
import 'package:provider/provider.dart';
▹ Open your main.dart file, delete the contents and then paste in the code below.
Points to Note:
- Line 8:
ChangeNotifierProvider
is the widget that provides an instance of aChangeNotifier
to its descendant widgets. Here it builds an instance of our model classSignInDetailsModel
. - Line 41: Our HomePage consists of a
Text
widget which is wrapped inside aConsumer
widget. The text displayed changes when a user is signed-in, theConsumer
widget forces a rebuild when a notification is broadcast by our model class. - Line 51: We define our Sign-In screen, consisting of a user name field, password field and a button.
- Line 84: Here we are using the
Provider.of
method to access the model instance.Provider.of
obtains the nearestProvider<T>
up its widget tree and returns its value. We then callsignIn
to crudely sign in the user and fire our notification, before finally transitioning back to the home screen. - Line 127: This is our model class, where we encapsulate our user name and sign-in date. Notice how we extend
ChangeNotifier
and then callnotifiyListeners()
when broadcasting to anyConsumer
widgets that a sign-in has occurred.
Summary
State Management in Flutter is a complex topic, not least because of the number of possible approaches and packages available.
Here, we have attempted to introduce the reader to Provider and its concepts and also demonstrated how to incorporate it into an application in order to share state between screens.
Whilst briefly mentioned in this post, the techniques demonstrated here also promote lose decoupling - whereby the home screen and the sign-in screens are unaware of each other’s inner workings. They share a common model and utilise Provider to propagate and consume changes in this model.