This article is included in Zhang Fengjietele’s public number programming king: article memory address F-S-P-01,
For more information, see << King of Programming Food Specification 1.0>>
The state management of Flutter is composed of three pillars. The Ming media should be the Provider of Flutter, which can be described as a master of Liu Bei’s level. As a slacker, save when you can. Provider is known to have a shuttle, beat the world invincible hand. However, brush these two moves, but be careful, otherwise the price is performance.
Provider of < XXX > (context). The data Provider. Of < XXX > (context). methodsCopy the code
One, a shuttle
The page is as follows, the first interface is four color blocks, click the blue word to jump to the purple interface
There are five operations: state synchronization, seemingly flawless on the surface, and a shuttle is OK and convenient, BUT!! —- scroll down.
1: open the interface 2: click the button, +1 3: click the blue block text, jump to the interface 4: click the purple block, trigger method 5: returnCopy the code
class CountState with ChangeNotifier { int _count = 0; get count => _count; void increment() { _count++; notifyListeners(); } } void main() { final count = CountState(); runApp(MultiProvider( providers: [ChangeNotifierProvider.value(value: count)], child: MyApp(), )); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( primaryColor: Colors.blue, ), home: new HomePage(), ); } } class HomePage extends StatelessWidget { const HomePage({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Wrap( spacing: 10, runSpacing: 10, children: <Widget>[ RedBox(),YellowBox(),BlueBox(),GreenBox(), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { Provider.of<CountState>(context).increment(); }, child: Icon(Icons.add), ) ); } } class RedBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------RedBox---------build---------"); return Container( color: Colors.red, width: 150, height: 150, alignment: Alignment.center, child: Text("Red:${Provider.of<CountState>(context).count}", style: TextStyle(fontSize: 20),), ); } } class YellowBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------YellowBox---------build---------"); return Container( color: Colors.yellow, width: 150, height: 150, alignment: Alignment.center, child: Text("Yellow:${Provider.of<CountState>(context).count}", style: TextStyle(fontSize: 20)), ); } } class BlueBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------BlueBox---------build---------"); return Container( color: Colors.blue, width: 150, height: 150, alignment: Alignment.center, child: InkWell( onTap:() { Navigator.of(context).push(MaterialPageRoute(builder: (context) => NextPage())); }, child: Text("Blue:${Provider.of<CountState>(context).count}", style: TextStyle(fontSize: 20)), ), ); } } class GreenBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------GreenBox---------build---------"); return Container( color: Colors.green, width: 150, height: 150, alignment: Alignment.center, child: Text("GreenBox:${Provider.of<CountState>(context).count}", style: TextStyle(fontSize: 20)), ); } } class NextPage extends StatelessWidget { @override Widget build(BuildContext context) { print("---------NextPage---------build---------"); return Scaffold( body: Center( child: InkWell( onTap: (){ Provider.of<CountState>(context).increment(); }, child: Container( color: Colors.purple, width: 150, height: 150, alignment: Alignment.center, child: Text("NextPage:${Provider.of<CountState>(context).count}", style: TextStyle(fontSize: 20))), )), ); }}Copy the code
The log print made me sweat. I wonder how many people abuse this shuttle for convenience.
In the second page (purple block), all five components trigger build. Four of them are invisible components.
-- -- -- - > [1] when it is open - I/flutter (24913) : -- -- -- -- -- -- -- -- -- RedBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- I/flutter (24913) : ---------YellowBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): -- -- -- -- -- -- -- -- -- GreenBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [no. 2. Click on the +, triggering method] - I/flutter (24913) : ---------RedBox---------build--------- I/flutter (24913): ---------YellowBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): -- -- -- -- -- -- -- -- -- GreenBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [3. Click on the blue block of text, jump interface] - I/flutter (24913) : ---------NextPage---------build--------- I/flutter (24913): ---------RedBox---------build--------- I/flutter (24913): ---------YellowBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): -- -- -- -- -- -- -- -- -- GreenBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [4. Click on the purple block, triggering method] - I/flutter (24913) : ---------NextPage---------build--------- I/flutter (24913): ---------GreenBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): ---------YellowBox---------build--------- I/flutter (24913): -- -- -- -- -- -- -- -- -- RedBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [5. Return] - I/flutter (24913) : ---------RedBox---------build--------- I/flutter (24913): ---------YellowBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): ---------GreenBox---------build---------Copy the code
Two, the Consumer comes to help
Use the Consumer to wrap the nodes that need to be updated. Process the four color blocks
Class HomePage extends StatelessWidget {// HomePage extends StatelessWidget... Consumer<CountState>(Builder: (CTX,state, Child)=> floatingActionButton (onPressed: () { state.increment(); }, child: Icon(icons.add),)); } } class RedBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------RedBox---------build---------"); return Container( color: Colors.red, width: 150, height: 150, alignment: Alignment.center, child: Consumer<CountState>(builder: (ctx,state,child)=> Text("Red:${state.count}", style: TextStyle(fontSize: 20),)), ); }} //Copy the code
Now look at the printout, what is the world suddenly much cleaner.
Consumers can specify small pieces of local consumption, avoiding a full refresh of the whole
-- -- -- - > [1] when it is open - I/flutter (24913) : -- -- -- -- -- -- -- -- -- RedBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- I/flutter (24913) : ---------YellowBox---------build--------- I/flutter (24913): ---------BlueBox---------build--------- I/flutter (24913): -- -- -- -- -- -- -- -- -- GreenBox -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [no. 2. Click on the +, triggering method] - no printing information -- -- -- -- > [3. Click on the blue block of text, jump interface] - I/flutter (26468) : -- -- -- -- -- -- -- -- -- NextPage -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [4. Click on the purple block, triggering method] - I/flutter (26468) : -- -- -- -- -- -- -- -- -- NextPage -- -- -- -- -- -- -- -- -- build -- -- -- -- -- -- -- -- -- -- -- -- - > [5. Return] - no printing informationCopy the code
You might say, boy, don’t you need to build for such a show? But you can’t gain weight without eating… Wait and see.
What does the Consumer do
1. Take a look at the source code comments:
Get Provider
from the ancestor and pass it to the component builder
Heirlooms that have been handed down from generation to generation are passed directly through the Consumer. No fancy passing.
Objective 2: Consumer@override // ERROR can be used when BuildContext is not used: ProviderNotFoundError because there is no Provider Widget build(BuildContext Context) {return ChangeNotifierProvider( builder: (_) => Foo(), child: Text(Provider.of<Foo>(context).value), ); } @override // OK Widget build(BuildContext context) { return ChangeNotifierProvider( builder: (_) => Foo(), child: Consumer<Foo>( builder: (_, foo, __) => Text(foo.value), }, ); } second, it helps with performance optimization through finer grained refactoring.Copy the code
2. The Consumer of the builder
You can see from the above that you actually create a build component, just a partial build
This allows for a finer granularity of the build, which naturally avoids unnecessary process and can be printed in The Builder to test. In other words, the build is just a Text rather than the entire RedBox.
class RedBox extends StatelessWidget { @override Widget build(BuildContext context) { print("---------RedBox---------build---------"); return Container( color: Colors.red, width: 150, height: 150, alignment: Alignment.center, child: Consumer<CountState>(builder: (ctx,state,child){ print("---------RedBox----Consumer-----build---------"); return Text("Red:${state.count}", style: TextStyle(fontSize: 20),); })); }}Copy the code
So how did he do it? It’s less than 20 lines. It inherits from the StatelessWidget
Build (key, child, and Builder); build (key, child, and Builder); You can see that the Builder method is responsible for this method. The T generic is the state model, which is also obtained by provider. of
(context).
class Consumer<T> extends StatelessWidget
implements SingleChildCloneableWidget {
Consumer({
Key key,
@required this.builder,
this.child,
}) : assert(builder != null),
super(key: key);
final Widget child;
final Widget Function(BuildContext context, T value, Widget child) builder;
@override
Widget build(BuildContext context) {
return builder(
context,
Provider.of<T>(context),
child,
);
}
@override
Consumer<T> cloneWithChild(Widget child) {
return Consumer(
key: key,
builder: builder,
child: child,
);
}
}
Copy the code
3. BuildContext you need to know
The question? Whose BuildContext is passed in?
As we all know, each Widget has its own Element, Element, and when the Element is mounted, it passes itself as a beautiful Context into the component’s or State’s build method for you to use. Here, break points are set inside the build of MyApp, the build of HomePage, the red build, and the red Consumer to get a glimpse of the four little angels.
So BuildContext is not what we think of as something that’s passed down from generation to generation. Rather, they are unique to each Widget, as are their genes, which are different within each Widget. So our problem is simple. Consumer is a Widget, and the context it provides is the context for Consumer. Let’s take a look at the position of the angels in the Element tree.
Again, Element is a concrete class that implements the BuildContext Abstract interface protocol. BuildContext passed in to Build in Widget or State is the corresponding Element of its component. Each Element records its parent, just like this, by following a BuildContext(Element), you can find its 18th ancestor, which should be 108. While the Widget pseudo-tree is very brief, the Element tree is not what you might think.
You may be wondering, who is the top patriarch? Let’s take a sneak peek at who the king of angels is.
Framework in is going to create a RenderObjectToWidgetElement, she is the root of all beauty. Next comes Provider, MultiProvider, and MyApp is two generations later.
4. What’s a Consumer?
How could a Consumer go straight over a father? When things get tough, quantum mechanics, take my Debug quantum cannon and trigger it on tap by setting a break point and taking a wave.
Breakpoint: 3 state-increment () red Consumer internal buildScope method when the button is first clicked: BuildScope :RawMaterialButton(dirty); increment(); ListenableProvider<CountState>(dirty, state: _DelegateWidgetState#ae650) RawMaterialButton(dirty, state: _RawMaterialButtonState#80335) _MaterialInterior(duration: 200ms, Shape: CircleBorder(BorderSide(Color(0xff000000), 0.0, BorderStyle. None)), elevation: 12.0, c break point line, come to [red inside Consumer] break point, the console has shown: I/ FLUTTER (13300): ---------GreenBox----Consumer-----build--------- I/flutter (13300): -- -- -- -- -- -- -- -- -- YellowBox, Consumer -- -- -- -- -- build -- -- -- -- -- -- -- -- -- side trip as the console display: again I/flutter (13300) : ---------RedBox----Consumer-----build--------- I/flutter (13300): -- -- -- -- -- -- -- -- -- BlueBox, Consumer -- -- -- -- -- build -- -- -- -- -- -- -- -- -- the interface has been updated. But before the button is delayed, god will go to buildScope, dirty table number is 1, _MaterialInterior(duration: 200ms, release again, button update, one click refresh interface is completed.Copy the code
For now, the only thing I care about is ListenableProvider. Who is that?
In this picture, you can already see the figure of the big guy. His father is MutiProvider.
In buildScope, our story takes place in the Rebuild method of ListenableProvider
Element#rebuild()=> ComponentElement#performRebuild()
When I see the InheritedProvider, I smile. It’s almost time to call it a day.
After the rebuild wave, five dirty tables are added, and each error is the node of Consumer. It’s only four. Why five?
“Consumer” was also added to the button in the last sentence. Flutter, as we all know, will only draw and reconstruct the elements in the dirty table. So you build the Consumer directly rather than the whole thing.
No harm without comparison. Finally, take a look at the dirty table when refactoring a page without Consumer. After the rebuild wave, the dirty table adds elements for the entire Widget.
That’s it, so for deeper levels, Consumer is recommended to reduce the granularity of updates.
conclusion
This is the end of this article. If you want to taste Flutter quickly, Flutter For Seven days is a must-have. If you want to explore it, follow in my footsteps and complete a Flutter tour.
In addition, I have a Flutter wechat communication group. You are welcome to join and discuss Flutter issues together. My wechat account is ZDL1994328. In addition, welcome to the king of public programming