Provider guide
Overall Workflow
- models are created using
classeswhich extendEquatable- models are used as Data Structures containing varaiables
- these variables are then used by
ExampleUserProvider with ChangeNotifierorBlocs
- these variables are then used by
- models are used as Data Structures containing varaiables
ExampleUserProvider with ChangeNotifierare for creating methods for changing states- create a private
-objnamevariable which ExampleUserProvider with ChangeNotifierwill also initialise this the above model- these methods have
notifyListenerswhich refreshes the UI when there is a state change i.e. when this method is called ExampleUserProvider with ChangeNotifieralso creates functions for size of data structure int the model, updating toggles, etc.
- create a private
MultiProvider(providers: [..])is called before theMaterialApp- is contains all the Providers user creates
ChangeNotifierProvider<Movies>( create: (context) => .., ..)will be inside theMultiProviderProvider.of<T>(context, listen: ..)- Using
Provider.of<T>()to consume data, listen to changes only if you need to, otherwise uselisten:false
- Using
Consumer(..)
Provider has Widgets
ChangeNotifierChangeNotifierProviderConsumerMultiProvider
1. create 'data model' class
data_model.dartseparate classes have separate files in folder📁 models
- models are created using
classeswhich extendEquatable - models are used as Data Structures containing varaiables and the user defined functions
- variables of the model are then used by
ExampleUserProvider with ChangeNotifierorBlocsvia a private object_objnameof theDataModele.g: `CounterModel _obj = CounterModel();
import 'package:equatable/equatable.dart';
class CounterModel extends Equatable {
int x = 0; // if this `x` is to be accessed,then the `CounterProvider` will have to create a private variable `_obj`
/// `CounterModel _obj = CounterModel();`
/// ' int get x => _obj.x; ' or ' int x() {return _obj.x} '
int get increment => ++x; /// `CounterProvider` can use these methods by: ⏬
/* void inc() {
_obj.increment;
notifyListeners();
}
*/
int get decrement => --x; /// `CounterProvider` can use these methods by: ⏬
/* void dec() {
_obj.decrement;
notifyListeners();
}
*/
List<Object?> get props => [x]; /// 'equatable package' needs this
}
2. class ExampleUserProvider extend ChangeNotifier a.k.a. data holder
we can call ExampleUserProvider data holder as it takes data from data model ExampleModel and gives that data to Consumer or Provider.of<>().
inside
📁 providers
e.g. counter_provider.dart file will have a class CounterProvider.
This CounterProvider class will create private _obj of the CounterModel to access the variables and methods of the CounterModel.
CounterModel _obj = CounterModel();
/// private variable `_obj` acts like a bridge between variables of `CounterModel` & `CounterProvider`.
int get x => _obj.x;
/// `_obj` is private becoz its only needed to access the value of variable in `CounterModel` and assign that value to a variable inside `CounterProvider`
creating private variables becoz these variable need not be accessed outside. As we create separate methods for the variables or values or methods which are to be accessed outside this class.
- e.g.:
var counter = Provider.of<CounterProvider>(context, listen: false);orvar counter = context.read<CounterProvider>();
- ⏫ this
counteris actually of TypeCounterProviderso we must access a variable which is directly inside theCounterProvider,- thus we will use the private variable
_objto get that value from theCounterModeland then return it by a separate function likeint get x => _obj.x;, here thisx .. CounterProvideris now having the value ofx .. CounterModeland so theCounterModeland then return it by a separate function likeint get x => _obj.x;, here thisx .. CounterProvidercan now be accessed byProvider.of<T>()and others.- So, this means the private variable
_objhere acts like a bridge/connection between the variables ofCounterModel&CounterProvider.
import 'package:flutter/cupertino.dart';
import 'package:../../models/counter_model.dart';
class CounterProvider with ChangeNotifier {
CounterModel _obj = CounterModel();
int get x => _obj.x;
void inc() {
_obj.increment;
notifyListeners();
}
void dec() {
_obj.decrement;
notifyListeners();
}
}
3. MultiProvider
MultiProviderhaschild: CupertinoApp(..),- contains all the Providers user creates
return MultiProvider(
providers: [
ChangeNotifierProvider<CounterProvider>( /// type `<CounterProvider>` is MUST
create: (context) => CounterProvider(), `(context) => ` is MUST
),
],
child: CupertinoApp(
debugShowCheckedModeBanner: false,
scrollBehavior: const MyCustomScrollBehavior(),
// home: Home(),
home: LearnProvider(),
),
);
4. ChangeNotifierProvider<ExampleUserProvider>( create: (context) => .., ..)
ChangeNotifierProvider<ExampleUserProvider>(
create: (context) => ExampleUserProvider(),
),
return MultiProvider(
providers: [
ChangeNotifierProvider<CounterProvider>( /// type `<CounterProvider>` is MUST
create: (context) => CounterProvider(), /// `(context) => ` is MUST
),
],
child: CupertinoApp(
debugShowCheckedModeBanner: false,
scrollBehavior: const MyCustomScrollBehavior(),
// home: Home(),
home: LearnProvider(),
),
);
The data accessed by the ChangeNotifier can be accessed by two ways
Provider.of(context)Consumer<CounterProvider>(builder: (context, counter, child) {return ...},),
4. Provider.of<T>()
Provider.of<T>() is used for interactions such as on button click: update a value inside ExampleUserProvider's variable.
Provider.of<CounterProvider>(context, listen: false)is same ascontext.read<CounterProvider>(). .. 🤯 MUST NOTE:listen:false
Using Provider.of<T>(context) to consume data, listen to changes only if you need to, otherwise use listen:false as Provider.of<T>(context, listen:false)
Provider.of<T>(context)is used for updating or changing or deleting the vlaues of variables or functions declared inside theCounterProvider
ElevatedButton(
onPressed: () {
// var counter = context.read<CounterProvider>(); /* this is also ✅ */
var counter = Provider.of<CounterProvider>(context, listen: false);
counter.inc(); /// `inc()` is a method inside `CounterProvider`
/* void inc() {
_obj.increment; /// `_obj` is of type `CounterModel` .. `increment() is .. ++x .. inside CounterModel`
notifyListeners();
},
*/
child: const Icon(Icons.add),
),
listen:false in Provider.of<T>(context, listen:false)
???? ???? ???? ????
5. Consumer
Consumer is used to consume/use data from the ExampleProvider whiich extends ChangeNotifier and display it in a Widget
Consumeris used for showing theWidgetson screen. These widgets are the ones which will have state change. all the Widgets which will have state changes MUST go intoCounsumerasreturnthe Widgets which will have different values or appearance based on other state changes.
Builder has 3 arguments:
contexttmpCounterProvider🤣🤣 I am naming it like this: it automatically becomes of typeCounterProviderjust like aprivate objectand this variable:tmpCounterProvidercan now access the variables ofCounterProviderchildoptional
/*
...
chid: .. */
Consumer<CounterProvider>( /// type `<CounterProvider>` is MUST
builder: (context, tmpCounterProvider, child) {
return Text('${tmpCounterProvider.x}'); /// here 'tmpCounterProvider' automatically becomes of type `CounterProvider` just like a `private object` and this variable: 'counter' can now access the varaibles of `CounterProvider`
},
),
When using
Consumerwidget, use the child option to mark part of the independent widget tree which need not rebuild.
Examples
import 'package:equatable/equatable.dart';
class MovieModel extends Equatable {
String movieId;
String movieName;
bool isFavorite;
String posterUrl;
MovieModel( {required this.movieId, required this.movieName, required this.posterUrl, this.isFavorite = false} );
void toggleFavorite() {
isFavorite = !isFavorite;
}
List<Object?> get props => [movieId, movieName, isFavorite, posterUrl];
}
class MovieProvider extends ChangeNotifier {
final List<MovieModel> _movies = [
MovieModel(
movieId: 'M1',
movieName: 'The Godfather',
posterUrl: 'https://lunkiandsika.files.wordpress.com/2011/11/the-godfather-alternative-poster-1972-01.png',
),
MovieModel(
movieId: 'M2',
movieName: 'The Notebook',
posterUrl: 'http://www.impawards.com/2004/posters/notebook.jpg',
),
];
List<MovieModel> get movies {
return _movies;
}
int get movieCount {
return _movies.length;
}
void updateFavorite(MovieModel movieItem) {
movieItem.toggleFavorite();
notifyListeners();
}
List<MovieModel> get favoriteMovies {
return movies.where((movie) => movie.isFavorite).toList();
}
int get favCount {
return favoriteMovies.length;
}
}