[Speaker] Let's pick up where we left off last time with Fitz.
We open up the app, check some boxes, and look at that:
Our app state is still there.
The boxes are able to be checked and unchecked.
Great.
But, wait, the progress bar at the top,
it's not changing
regardless of the boxes that I've checked off.
What gives?
♪ [music] ♪
In the last episode,
Fitz talked about how Flutter widgets are immutable.
They don't change as we're using them in our app.
They aren't part of an app,
but they describe exactly how to build an app,
like a recipe for building a cake.
Then came the idea of app state,
any data that would determine how our app's UI appears
when users interact with the app.
The state-less task items were converted into stateful widgets,
and state was introduced
to our exploration planner app.
So we try to run our app and check off a few boxes,
but just as before, the progress bar at the top
isn't, well, progressing.
Taking a look at the code, we can see why.
The progress bar doesn't have any state attached.
The state for each checkbox is independently stored
within each task item.
How does the progress bar know about those task item states?
In other words, how does the widget over there
get access to the state that's stored over here?
The answer is, it doesn't, yet,
and that's where state management comes in.
If state is the data that determines our app's UI,
then state management is how we organize our app
to most effectively access state and share it across widgets.
Now, before we continue, I want to acknowledge
that state management is a very hot topic in the dev community,
regardless of framework.
There's always many solutions available,
and everyone seems to have the one that works best for them.
State management has two goals.
One, provide access to data,
and two, tell widgets
that they need to be redrawn when the data changes.
State management is the guardrail layer in between
that gives us the ability to do both of those things.
That's it.
What's the best state management solution for Flutter?
Well, it depends.
There isn't one single best option.
There are many options available for state management in Flutter,
and at the end of the day,
you'll probably be happy
with just about any of the popular libraries.
If you're feeling adventurous, try out a few of them
and decide what works best for you and your projects.
Due to time constraints, we'll only be touching
on one of Flutter's many state management solutions
in this video.
For the progress bar to know about the task item states,
we'll want to lift the task item states up in the widgets tree
so that the progress bar has access to the task item state.
Enough talking, let's get some state management into our app
by adding the flutter_riverpod package to our app.
Riverpod is one of the many state management packages
available for Flutter.
Riverpod is built on the premise of having providers or an object
that encapsulates a piece of state,
also known as data, and allows listening to that state,
and consumers, which are objects that allow developers
to interact with and read from providers.
Instead of storing each task state within the task item,
we can give it to a provider that we've called TaskProvider
to manage its state.
Specifically, we're using a state notifier provider,
which consumers can listen to.
It's Riverpod's recommended solution for managing state,
which may change in reaction to a user interaction.
Perfect for our task items.
When initializing a provider, we gave it a list of tasks,
along with a task notifier,
which is used to allow the UI to modify state.
From there, we'll go to our progress and task list widgets
and convert them from state-less and stateful widgets
into consumer widgets.
Consumer widgets are a special kind of stateful widget
that lets you access providers
by giving you access to this ref object.
Within the consumer widgets, we can now watch the task provider
for any change in these task state.
Then, whenever there's a change in state,
that widget and all of its children,
will get rebuilt to reflect our new app state.
Plus, because the provider is declared as a global object,
other widgets in your app will also have access
to the provider and task state.
Now, whenever we check a checkbox,
we'll read from our task provider notifier
and toggle the task.
So, what else has changed in our app?
Well, we've introduced a task class
to make it easier to keep track of individual tasks.
We added a task notifier
so that UI interactions can modify our task state.
We also introduced Riverpod to manage state,
which means instead of using the setState syntax,
we use slightly different syntax
to access the state stored in our provider.
We also wrapped our entire app in a provider scope,
a widget that stores all of our providers.
Plus, due to our task list being stored as state
with the provider,
our task list build code is significantly more condensed.
We now iterate through the list of tasks
instead of hard coding each item.
The data architecture for our app
has been changed with the introduction of Riverpod.
State has been moved out of the widget to our provider,
but that's it.
Aside from that, the recipes for each widget remain the same.
Now that we've gotten our exploration planner working,
in the next episode, we'll take a look
at how we can make our exploration planner app look better.
See you over there.
♪ [music] ♪