[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] ♪
Riverpod ftw