1. What is a provider
Provider is used by Google to replace the previous state management Provider. Although there is only one letter difference, the usage is very different. Flutter provides a variety of Providers for different types of objects; Providers also use InheritWidget to put the shared state on the top-level MaterialApp.
Provider can provide you with:
- Simplify resource allocation/disposition
- lazy loading
- Significantly reduces boilerplate when making a new class every time
- devtools-friendly
- Common ways to use these InheritedWidgets (see Provider.of/Consumer/Selector)
- The complexity of the listening mechanism grows exponentially, increasing the scalability of the class (e.g. ChangeNotifier, which is O(N²) for scheduling notifications)
Second, the use of Provider components
Because the project uses provider: ^4.0.4, this version is the main one.
Now that it is 5.0.0, I probably took a look at the update and optimized it a lot. If you are interested, you can go and have a look.
Basic usage of Consumer
Create a new data provider class, which is mainly used to provide data
class DataProvider extends ChangeNotifier{
int count=0;
int count1=0;
void add(){
count++;
print("count:$count");
notifyListeners();
}
void add1(){
count1++;
print("count1:$count1");
notifyListeners();
}
}
```bash
return ChangeNotifierProvider(
create: (_) => DataProvider(),
child: Column(
children: <Widget>[
Consumer<DataProvider>(builder: (_, data, __) {
print("text1 rebuild");
return Text(data.count.toString());
}),
Consumer<DataProvider>(builder: (_, data, __) {
print("text2 rebuild");
return Text(data.count1.toString());
}),
Consumer<DataProvider>(
builder: (_, data, __) => GestureDetector(
onTap: () {
data.add();
},
child: Container(
margin: EdgeInsets.all(30),
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Text("我增加了count"),
),
),
),
Consumer<DataProvider>(
builder: (_, data, __) => GestureDetector(
onTap: () {
data.add();
},
child: Container(
margin: EdgeInsets.all(30),
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Text("我增加了count1"),
),
),
),
],
),
);
operation result:
I/flutter (24104): count:1
I/flutter (24104): text1 rebuild
I/flutter (24104): text2 rebuild
Other ways to write consumer
class ChildAWidget extends StatelessWidget {
final Widget child;
final dynamic data;
ChildAWidget({
this.child, this.data});
@override
Widget build(BuildContext context) {
print("ChildAWidget build");
return Container(
child: Column(
children: <Widget>[Text("$data" ?? "ChildAWidget"), child],
),
);
}
}
class ChildBWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("ChildBWidget build");
return GestureDetector(
onTap: () {
Provider.of<DataProvider>(context).add();
},
child: Container(
child: Text("点我啊"),
),
);
}
}
return ChangeNotifierProvider(
create: (_) => DataProvider(),
child: Consumer<DataProvider>(
builder: (_, data, child) => ChildAWidget(
child: child,
data: data.count,
),
child: ChildBWidget(),
),
);
In this example, ChildBWidget is redrawn outside the builder. Then the ChildAWidget instance is passed to the builder as the last parameter.
This means that when the builder is called repeatedly, the Consumer does not create new instances of ChildBWidget. This lets Flutter know that ChildBWidget doesn't have to be repainted. So with such a writing method, when the DataProvider is updated, only ChildAWidget will redraw.
selector basic usage
return ChangeNotifierProvider(
create: (_) => DataProvider(),
child: Column(
children: <Widget>[
Selector<DataProvider, int>(selector: (_, store) => store.count, builder: (_, data, __){
{
print("text1 rebuild");
return Text(data.toString());
}
}),
Selector<DataProvider, int>(selector: (_, store) => store.count1, builder: (_, data, __){
{
print("text2 rebuild");
return Text(data.toString());
}
}),
Consumer<DataProvider>(
builder: (_, data, __) => GestureDetector(
onTap: () {
data.add();
},
child: Container(
margin: EdgeInsets.all(30),
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Text("我增加了count"),
),
),
),
Consumer<DataProvider>(
builder: (_, data, __) => GestureDetector(
onTap: () {
data.add1();
},
child: Container(
margin: EdgeInsets.all(30),
decoration: BoxDecoration(border: Border.all(color: Colors.red)),
child: Text("我增加了count1"),
),
),
),
],
),
);
operation result:
I/flutter (24104): count:1
I/flutter (24104): text1 rebuild
other wording of selector
return ChangeNotifierProvider(
create: (_) => DataProvider(),
child: Selector<DataProvider, int>(
builder: (_, d, child) => ChildAWidget(
child: child,
data: d,
),
shouldRebuild: (pre,next)=>pre!=next,
selector: (BuildContext c, DataProvider d) {
return d.count;
},
child: ChildBWidget(),
),
);
If you want to use multiple providers, you can use MultiProvider.
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: DataProvider()),
ChangeNotifierProvider.value(value: DataProvider1()),
],
child: ChildCWidget(),
);
3. Summary
In fact, there is not much difference in usage between consumer and selector. The difference is that as long as the value of consumer changes, it will notify its host to refresh the UI, while selector can select some values to update. The granularity of selector control is finer than that of consumer. Consumer monitors the changes of all data in a provider, while selector monitors the changes of one or more values. It can be seen from the above operation results.
There are still some provider components that are not introduced, and I basically write them only when I use them in projects. Alright, I'm done with Sahua, and I've recorded another learning process.