Book connected to wen
Management of Flutter state: In Provider4 Introductory Tutorial (I), we have a preliminary understanding of state management and Provider, and also learn the use of ChangeNotifierProvider and Consumer. However, due to the limited time, we shut off the mention of Consumer, and even forgot to hit the advertisement. Now let’s begin by talking about consumers.
Why is it Consumer?
Consumer2-6
If you are careful about using Consumer, you may notice that there are several magic classes:
Consumer2
Consumer3
Consumer4
Consumer5
Consumer6
Consumer
Not really. This is actually the Consumer family, consisting of six members. The number after “Consumer” represents the number of data classes a Consumer can receive.
Oh, my God, that’s what it means. So why isn’t there a Consumer1000? Dear, I suggest you to ask the author, I am afraid of being killed. So if there is such a need, do it yourself.
Speaking of which, let’s talk briefly about consumers.
The entire Consumer family uses the Builder pattern and notifies the Builder when a Consumer receives an update. And builder is actually a Function, also can be said to be a lamda. In the case of Conumer, its Builder is defined as Widget (BuildContext Context, T model, Widget Child), which takes three parameters, where T is the data type that can be received, T is the generic T in Consumer
, so make sure you provide generics when using Consumer. In the previous code, for example, Consumer
means that the Consumer receives the MyChangeNotifier instance. Similarly, Consumer2
can receive both A and B data, as can be seen from its Builder definition: Widget Function(BuildContext Context, A value, B value2, Widget Child).
,b>
The Child in Builder is used to build parts that are irrelevant to the data model, and is not redrawn in multiple runs of Builder.
By definition, it’s time to say Consumer3, but considering that they’re all pretty much the same, I won’t add up to the word count. See the source code for details.
Why recommendedConsumer
As I said earlier:
There is no magic in Consumer itself, no fancy implementation. Simply use provider.of in a new control and delegate the build method of that control to the Builder. This Builder will be called multiple times. It’s that simple.
But I also said that Consumer can give us performance optimization because Consumer can give us granular redraw.
Remember that when using Provider. Of, unless we set Listen :false, the BuildeContext related controls in Provider. Of are rebuilt whenever the data received in Provider. This is of course the desired behavior, but sometimes it can cause unnecessarily excessive redrawing.
Let’s look at an example:
@override
Widget build(BuildContext context) {
return FooWidget(
child: BarWidget(
bar: Provider.of<Bar>(context),
),
);
}
Copy the code
In the above code, only BarWidget relies on the data returned in provider.of. However, when the Bar changes, both the BarWidget and FooWidget are redrawn.
Ideally, only barWidgets should be redrawn. One solution is to use Consumer.
To implement the approach we just described, we’ll wrap controls that rely on the Provider with Consumer.
@override
Widget build(BuildContext context) {
return FooWidget(
child: Consumer<Bar>(
builder: (_, bar, __) => BarWidget(bar: bar),
),
);
}
Copy the code
Now, if the Bar is to be updated, only the BarWidget is redrawn. But what if the FooWidget relies on a provider? Such as:
@override
Widget build(BuildContext context) {
return FooWidget(
foo: Provider.of<Foo>(context),
child: BarWidget(),
);
}
Copy the code
Remember what I said about Child? Yes, that’s it, go to the code:
@override
Widget build(BuildContext context) {
return Consumer<Foo>(
builder: (_, foo, child) => FooWidget(foo: foo, child: child),
child: BarWidget(),
);
}
Copy the code
In this example, the BarWidget is redrawn outside of the Builder. The BarWidget instance is then passed to the Builder as the first parameter.
This means that when Builder is called repeatedly, the Consumer does not create new instances of BarWidget. This lets Flutter know that it does not have to redraw the BarWidget. So in this way, when Foo is updated, only the FooWidget is redrawn.
If you are interested, you can implement the above sample code and add a few print to build() to verify that this is true.
Consumer summary
In summary, a Consumer has two main uses:
- The Consumer allows us to get data from the Provider when there is no Provider specified in our BuildContext.
- Improve performance
Therefore, I strongly recommend using Consumer because it can improve performance. Flutter does have many optimizations, but many are not the whole story. Performance improvements are more dependent on how we implement Flutter.
MultiProvider
We have been talking about the situation of one Provider per page, but in fact, many large-scale applications, it may be more than one Provider, dozens of providers is also possible, then we will want to nest large method:
Provider<Something>(
create: (_) => Something(),
child: Provider<SomethingElse>(
create: (_) => SomethingElse(),
child: Provider<AnotherThing>(
create: (_) => AnotherThing(),
child: someWidget,
),
),
),
Copy the code
Is a sentence that begins with an F and ends with a U about to pop out again? Don’t worry, the authors have thoughtfully designed MultiProvider:
MultiProvider(
providers: [
Provider<Something>(create: (_) => Something()),
Provider<SomethingElse>(create: (_) => SomethingElse()),
Provider<AnotherThing>(create: (_) => AnotherThing()),
],
child: someWidget,
)
Copy the code
Of course, these two pieces of code are exactly equivalent, so there’s no dark magic in MultiProvider, except that it doesn’t make the code look ugly.
Note: Consumer can also be used in the MultiProvider. But the Consumer must return the Child it created in the control tree, the Builder in the Builder.
MultiProvider(
providers: [
Provider(create: (_) => Foo()),
Consumer<Foo>(
builder: (context, foo, child) =>
Provider.value(value: foo.bar, child: child),
)
],
);
Copy the code
I have a question
When we talked about MultiProvider, a question popped into our head:
- Can be found in a
BuildContext
To get more than one of the same data typesprovider
?
It looks like this in code:
Provider<String>(
create: (_) => 'China',
child: Provider<String>(
create: (_) => 'dalian', child: ... ,),),Copy the code
The answer is no. When there are multiple providers of the same data type, a control can obtain only one: the nearest provider.
So, we have to be very specific about their type:
Provider<Country>(
create: (_) => Country('China'),
child: Provider<City>(
create: (_) => City('dalian'), child: ... ,),),Copy the code
Emmmm, it looks good.
For more details, please listen to the next episode
As the second installment in the Provider series, the content is still simple, and I’m running out of time.
To be continued… I don’t expect you to decide.