The ChangeNotifierProvider Widget
Explore how to use ChangeNotifierProvider in Riverpod for efficient Flutter state management. Understand its autoDispose feature to reduce widget rebuilds and manage app state with less boilerplate. Learn to build apps that update only necessary widgets, improving performance and structure.
Riverpod’s ChangeNotifierProvider
In the previous lesson, we combine both the Riverpod and Provider packages. We’ve seen the most critical part of state management with Riverpod. We mixed Riverpod and the old Provider package to have some coding fun. We also checked how we could reduce widget rebuilds.
We have also discussed Provider's ChangeNotifierProvider in great detail previously. The new Riverpod state management package also uses the same ChangeNotifierProvider but with a particular flavor, making it more advanced.
Provider's ChangeNotifierProvider
Let us summarize and state the ChangeNotifierProvider's main points in the Provider package.
We have seen and used such cases in our early examples. A model class that extends ChangeNotifier can call notifyListeners() any time the state in that class is changed or updated.
In a model-view-controller pattern, we notify the UI to rebuild the layout as our data gets updated in the view folder. That is done through ChangeNotifierProvider.
ChangeNotifierProvider in the Provider package is a kind of provider that provides the updated state object to any place in the UI layout. If we’ve already used ChangeNotifier, it becomes much easier for us than before. Now we can do the same thing with the help of ChangeNotifierProvider and the Consumer widgets.
But in Riverpod, the role of ChangeNotifierProvider has changed completely. Riverpod’s ChangeNotifierProvider helps us reduce widget rebuilds and allows us to autoDispose.
What is autoDispose?
The main concept of autoDispose is to destroy the state of a Provider when it is no longer in use.
The ChangeNotifierProvider in Riverpod comes with the autoDispose feature. That is the most significant advantage of ChangeNotifierProvider in Riverpod. The use of autoDispose is also the main difference between the ChangeNotifierProvider here and that of the old Provider package. We’ll discuss this difference, but before that, we must briefly examine why autoDispose is essential.
There are multiple reasons for using this autoDispose method with ChangeNotifierProvider in Riverpod:
- It helps to close the connection when using
Firebaseand therefore avoids unnecessary costs.- It resets the state when the user leaves a page and re-enters it.
As previously learned, in the Provider package, the role of ChangeNotifierProvider was to provide an instance of a ChangeNotifier to its descendants. Since it is a widget, it can have many descendants. We can directly send the updated data to the bottom-most widget without rebuilding the widget tree.
However, in Riverpod, the whole scenario takes a different route. Not only it helps us to autoDispose, but it also simplifies the entire experience. To demonstrate the advantage of ChangeNotifierProvider in Riverpod, we will make a simple app where we can add as many strings as we want by pressing the button.
If you have built an e-commerce application with Flutter in the past, we’ll apply the same logic to add items to the cart. To have a clear picture, let’s see the below image first:
Since we have not used the typical model-view-controller pattern this time, this ChangeNotifierProvider has many descendants. Still, the panel in the image below shows how it helps us reduce the widget rebuilds:
Of course, while building the app, we would certainly avoid this process. We’ll make our Flutter application structure more loosely coupled.
App entry point
It’s our main method, this time. We haven’t used the ProviderScope to make it look simple.
The Model class and ChangeNotifier
Now we can define our model class that extends ChangeNotifier.
Nothing very exquisite. Except that we have used this line:
We have seen the rest part in our Provider package also.
Wrapping state objects in the Provider
This is our main challenge. We should be able to access that state object in multiple locations. At the same time, keep in mind that we should structure our app to reduce widget rebuilds.
Now the syntax becomes much more straightforward than before. We can easily combine this object with others. Like any other Provider, the ChangeNotifierProvider widget ensures one thing. The widget that reflects the updated data only gets recomputed.
Let’s see how state management becomes much more accessible than before with less boilerplate code.
Once we start watching, we can use ListView.builder() to show the items. Adding items also becomes easier than before.
The following is the complete application in action:
name: riverpod_examples
description: A new Flutter project.
# 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
flutter_riverpod:
provider:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_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
After examining the ChangeNotifierProvider application above and in previous lessons, we realize that we can use Riverpod’s Provider in many ways.