Before diving deep into the Provider package, let’s understand what state management is.
For better understanding, we’ll take a simple shopping app for reference.
In the above image, we can see that there is an app bar, a drawer, a cart, a favorites section, an orders section, and products.
Firstly, let’s decrypt the above image.
- At the very top, there’s MyApp class which is the entry point of our app. Then it’s divided into 4 paths which lead to separate classes and functionality homepage, cart, favorites, orders. Further away, the homepage extends to provide us the app bar and access to product items.
- Now, if we add a particular product as a favorite or as a cart item then we need to call setState() and update the UI of the Favorites screen or the cart screen respectively. Not to forget about passing the data along when clicking on favorites or the cart icons. By the data we passed, we can now display the products which were marked as favorite or added to the cart. But sometimes it becomes janky/messy while managing state, passing data, updating the UI, etc. all at once.
- In short, all our data should be in the topmost widget/class (in this case MyApp), and bypassing it through arguments from widget to widget becomes kind of messy. Also, performant issues can arrive as if any smaller widget needs data, the whole parent class will change its state. So to prevent it, we use the Provider package.
- Implementation of the Provider package is pretty simple. Firstly, select the class of which, the data should be shared among the whole app. Then simply add ChangeNotifier class in mixin form with the ‘with’ keyword.
In the case of inheritance, we instance the whole class into another class. While if we add a mixin to a class, it is just instance properties and methods of the class added.
- As shown in the above image, YourClassName mixes with ChangeNotifier class. It is a special class provided by the Provider package.
- Now, we can also see notifyListeners() method which comes with ChangeNotifier class. What this function does is that if there is some change in data in the class where ChangeNotifier is mixed(in this case YourClassName), it will call the method and update the data and reflect it on the UI. Taking in mind the example of our e-commerce app, let’s understand this method.
- Let’s say there is a class named Products. In that, there are several methods like markAsFavorite(), addToCart(), etc. Now, the products must be a map of properties of each product like price, description, title, id, isFavorite, etc.
- And when we call markAsFavorite() method, a new map will be formed containing only those products marked as favorite. This whole logic will be in this method and at last, there will be notifyListeners() which will tell the app state to change its data accordingly. This data can be used to render only those products which are marked favorite.
- This was in class. But, how do we implement it in our widgets? Well, as mentioned in the above image, first, we declare a variable and assign the data to it by fetching it through the provider. Its syntax is similar to that of media query if you've used it in flutter. Likewise, it uses context to get the location from where the data is stored.
- Since it’s of a generic type, we have to provide what type of value/data we want. Like in Row/Column widget we mention . Moving ahead, we can provide our class name in which the data is stored and the same data which we want to retrieve. And just like enums in the dart, we can access a specific part of data as shown in the image.
- Now, you may wonder that what is this listen: false mentioned in the below image even though the syntax is the same as above.
- The difference is that: If we mention listening to false, it will not re-run every time some data is changed. This is useful when you just wanna display or store the data in the app. As it won’t require rebuilding the whole app, it will be beneficial to performance.
- Now, time to understand one of the very main concepts of implementing the provider package. As mentioned earlier, we need to provide the providers carrying data at the topmost level of our app. So it can be easily passed down through the whole app.
As shown in the image above, you can wrap your MaterialApp or entry widget inside another class provided by the provider package. The ChangeNotifierProvider class, which will have all the providers you need pointing to the respective class mixed with ChangeNotifier. This was the case for a single provider throughout the app.
There’s also a scenario where you’ll see ChangeNotifierProvider.value as in the below image.
- The simple justification is that when you first initialize the class, it is better to use create method. After that, when every time you call ChangeNotifierProvider, you just need to call its value rather than the whole function which is already initialized in the main file.
- Moreover, in many cases, you may have more than one provider in your app. The below-mentioned method would be the way to execute it.
- Yes, there is a MultiProvider class. It basically consists of the list of providers and again same rules apply here. If you’re setting up the class for the very first time, you’ll need to use create method.
Last but not the least, this will be the final topic covered in this blog post.
- Consumer as from the syntax can be seen that is very much like Provider. And indeed it is. But it has its own benefit. Let’s see how.
- So, there are many circumstances when we just need data for a specific child widget and not for main or bigger widgets. Hence it doesn’t make sense to use the provider in that child’s parent widget.
- The reason is very simple. It will just rerun the whole parent widget unnecessarily whenever there is a change in its child widget.
- To sum it up, Consumer will only rebuild the widget it is attached to. In this case, the IconButton widget. So, this was all about the Provider package and its implementation.