The Provider Library Fundamentals
Explore how to manage state in Flutter apps using the Provider library. Learn to set up ChangeNotifierProvider, use models for state changes, and minimize widget rebuilds in complex widget trees for better app performance.
We'll cover the following...
What is the Provider package
What is the Provider in Flutter?
The Provider is a Flutter package. It’s a wrapper around the InheritedWidget.
Before we proceed, we must add the dependency on the provider to the pubspec.yaml file.
It looks like this:
Now you are set to use the provider to manage a state in your app.
What is a state? The state is something that exists in memory. When the app is running, in most cases, we will try to manage the state. The Provider helps you to do that.
The state itself is critical, while efficiently managing it is essential too.
That’s because managing state is crucial to building any complex app that handles multiple screens, different variables, and user sessions. Hence, you should plan it.
Flutter creates a deep and nested widget tree. To manage a state at the lowest bottom, we cannot rebuild every top widget. That is wasteful memory consumption.
So, how can we solve this problem? The Provider package in Flutter is the answer!
Now let us do some coding together. Our goal is to understand the main concept of Flutter Provider.
Using ChangeNotifierProvider is one solution. It comes from the provider package, and it provides an instance of a ChangeNotifier to the widgets. So now Flutter has in-built mechanisms for widgets to provide data and services to their distant descendants.
Widget is very powerful. ChangeNotifierProvider uses that power to notify the listeners.
How does it do that?
By making it possible to place it on top of our widget tree.
It creates a context that returns the model from where the data comes from, and it also returns a child widget, which will consume the data.
Why do we need a model?
We need it for a simple reason. First of all, it will store and give us changed data. Although the data is temporary or short-lived in this example, we still need a small model class.
Now we can provide this designed model to the desired widget. However, our desired widget positions itself at the lowest point.
While sending our data directly to the lowest point, we shouldn’t rebuild the top widgets.
The AVeryDeepWidgetTree class is a long code snippet.
Let us now understand how it works. Hint: It’s something to do with a deeply nested widget tree.
As explained earlier, Flutter allows nested widget trees. We cannot imagine a complex app without that.
Here is the snapshot of the app we are trying to build:
Let us see the code now. Then I’ll explain what is happening.
Remember our model class. We have only one variable that will reflect the state change. So at the bottom-most widget, we need to access it.
We will use the Provider.of() method, where we will mention the type of our model and pass the context.
The Provider helps us to access the model data and method anywhere inside that widget tree.
When we click the button, it will change the state of the app by increasing the number.
The main drawback of the above code hides in this line of Code:
We want to rebuild only the text part where the number will show itself, and the floating action button section.
To achieve that, all we need to do is write a separate widget for those parts. After that, we will call it inside our view.
Next, we will add the following widget to the AVeryDeepWidgetTree:
That’s all. When we press the button and change the state, running the code will only change a tiny segment of the widget.
We can test this in the code widget given below:
name: basic_flutter_provider
description: A new Flutter project.
###Clear1
# The following line prevents the package from being accidentally published to
# pub.dev using `pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
provider:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.1
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
We have successfully removed the pitfalls of rebuilding the whole widget tree. Instead of changing the state for the whole tree, we only change the state for that widget section. It does not affect the parent widgets and avoids widget rebuilding.
In any ideal Flutter App, it is better to avoid widget rebuilding, as much as possible.
The advantage of the Provider or Riverpod package is that they help us avoid widget rebuilding.