This is the 8th day of my participation in the First Challenge 2022
Past wonderful
👉 Flutter will Know will Series – What exactly are Three Trees
👉 Flutter must know must know series — Update reuse mechanism of Element
We have already learned about the renewal of three trees and Element, all of which are somewhat theoretical. This article will examine the previous knowledge points from several practical examples.
Here is the text
Case description
The basic function of the case is to click a button to exchange color blocks. Detailed code for the case can be found at 👉 case code
The core code is as follows:
StatelessColor
Use the StatelessWidget to display color blocks as follows:
class StatelessColor extends StatelessWidget {
final Color defaultColor = UniqueColorGenerator().getColor();
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100,
child: Container(
color: defaultColor,
),
);
}
StatelessColor({Key? key}) : super(key: key);
}
Copy the code
StatefulColor
Use the StatefulWidget to display color blocks as follows:
class StatefulColorfulTileState extends State<StatefulColor> {
final Color defaultColor = UniqueColorGenerator().getColor();
@override
Widget build(BuildContext context) {
return SizedBox(
height: 100,
width: 100, child: Container( color: defaultColor, ), ); }}Copy the code
UniqueColorGenerator
Randomly generate colors with the following code:
class UniqueColorGenerator {
List<Color> colorList = [
Colors.blue,
Colors.yellow,
Colors.red,
Colors.black54,
Colors.greenAccent,
Colors.pinkAccent
];
Random random = Random();
Color getColor() {
return colorList[random.nextInt(6)]; }}Copy the code
Case 1: Swap Stateless Color
Let’s look at the code in action:
class _SwapColorDemo1State extends State<SwapColorDemo1> {
late List<Widget> widgets;
@override
void initState() {
super.initState();
widgets = [StatelessColor(), StatelessColor()]; / / first place
}
swapTile() {
setState(() {
widgets.insert(1, widgets.removeAt(0)); / / the third place
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stateless'),
),
body: SafeArea(
child: Row( / / in the second place
children: widgets,
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.swap_horiz),
onPressed: swapTile, / / the third place)); }}Copy the code
This is a very simple page with two random color blocks of 100 by 100. Click the button in the lower right corner to swap color blocks and refresh the page. \
First code: When the page is initialized, initialize two color blocks. The second piece of code: Use the Row component to wrap two color blocks. The third code: click the button, refresh the page, to achieve the function of exchanging color blocks
widgets.insert(1, widgets.removeAt(0)); A B widgets. RemoveAt (0) get A, and array B insert in1Insert the sign bit into A and the result is B, A and the same thing happens.Copy the code
Do you think you can switch? Must be ok 😄.
Here’s why. From our previous articles, we learned that a Widget is the UI configuration to display, and Element actually displays the UI based on the Widget. Since our colors are in the Widget, we can swap blocks in the following ways:
Element’s widgets point to new widgets, such as the yellow widget Element used to point to in pink.
Rebuild the Element based on the swapped widgets. For example, destroy the Element that originally hosted the red Widget and generate an Element based on the yellow Widget.
Swap elements directly. For example, swap the red and yellow Element positions
Let’s take a look at which of these cases our code falls into.
When we click the button, it will trigger the build method, and then trigger a Row control update, Row corresponding Element is MultiChildRenderObjectElement, its update will be executed to 👉 updateChildren method.
The child component of a Row looks like this:
So in the updateChildren method, we just stay in the first step of the loop —– diff down and update the child nodes
Because widget. canUpdate returns true. The runtimeType of the old and new widgets is StatelessColor, and since we did not set the Key manually, the Key property of the old and new widgets is null.
So canUpdate returns true. The framework thinks it needs to reuse Element, so it updates the child.
👇 let’s see how to update child.
Here’s a reminder of the updateChild table we talked about earlier:
The input information in Case 1:
Child is the old Element newWidgets is the widget to display, and here is our custom widget, swapped widget
Check the update table above and perform the logic of reusing updates.
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
   ...
   // Key and runtimeType can be reused in the fourth placechild.update(newWidget); . }Copy the code
It’s executing the logic of the fourth code. Child is the Element that carries the StatelessColor, and StatelessColor is the StatelessWidget, so child is the StatelessElement.
Remember the update logic for StatelessElement? Is to call the build method of its Widget.
The displayed UI is the UI returned by the Build method of the Widget object that Element holds. We store the color information in the Widget, so we swap colors normally.
The bottom block represents Element and the text represents Widget. Element does not change, only the Widge t pointing to it is changed, which is why the color swap occurs.
summary
StatelessWidget color replacement reasons:
- Reuse Element and update the Widget that Element points to
- Widgets are stateless widgets in which colors are stored.
Case 2: Exchange a Stateful Color
The body code and function of the page remain the same 👉 detailed code, just replace StatelessColor with StatefulColor.
Phenomenally, the blocks are not swapped.
Let’s take a look at why the color does not change after switching from a StatelessWidget to a StatefulWidget.
Similar to the analysis in Case 1, the update Hildren method is executed.
And in the updateChildren method, we again just stay in the first step of the loop —– diff down and update the child nodes.
With case 1, we went straight to updateChild, except that the type of each child changed from StatelessElement to StatefulElement. The call process is as follows:
Unlike StatelessElemen t in Case 1, the UI generated by Element is the build method of the State object, and our colors are stored in State, which is initialized when Element is initialized. The following code:
StatefulElement(StatefulWidget widget)
 : _state = widget.createState(),
   super(widget) {
 state._element = this;
 state._widget = widget;
}
Copy the code
That is, the Element and the State object live and die together. Element is reusable, so Element’s State reference is constant, and our color is stored in the State object, resulting in: Element simply replaces widgets with the same color.
The update process is as follows:
summary
Reasons for not replacing StatefulWidget colors:
- Reuse Element and update the Widget that Element points to
The State reference held by Element has not changed
, the color is stored in the State object
Case 2: Exchange a Stateful Color
The body code and function of the page remain the same 👉 detailed code, just replace StatelessColor with StatefulColor.
Phenomenally, the blocks are not swapped.
Let’s take a look at why the color does not change after switching from a StatelessWidget to a StatefulWidget.
Similar to the analysis in Case 1, the update Hildren method is executed.
And in the updateChildren method, we again just stay in the first step of the loop —– diff down and update the child nodes.
With case 1, we went straight to updateChild, except that the type of each child changed from StatelessElement to StatefulElement. The call process is as follows:
Unlike StatelessElemen t in Case 1, the UI generated by Element is the build method of the State object, and our colors are stored in State, which is initialized when Element is initialized. The following code:
StatefulElement(StatefulWidget widget)
 : _state = widget.createState(),
   super(widget) {
 state._element = this;
 state._widget = widget;
}
Copy the code
That is, the Element and the State object live and die together. Element is reusable, so Element’s State reference is constant, and our color is stored in the State object, resulting in: Element simply replaces widgets with the same color.
The update process is as follows:
summary
Reasons for not replacing StatefulWidget colors:
- Reuse Element and update the Widget that Element points to
The State reference held by Element has not changed
, the color is stored in the State object
Case 3: Swapping a Stateful Color with a Key attribute
Before going into details, we introduce a point Slot information.
Slot Indicates the Slot information
The slot point refers to an Element’s position on its parent node. If the parent Element is a single-node Element, the slot point on the child node is null.
IndexedSlot is an enclosing class that encapsulates the index and slot values. The slot value of the first child node is null. The slot value of the second child node is the previous node, and so on. , so through the slot point, you can get the position.
The groove point is determined in the mount method.
Slot points of a single node:
Slot points of multiple nodes:
During the updateChild process, we had a judgment point to compare slot information.
///Omit code
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
 final Element newChild;
 if(child ! =null) {
  bool hasSameSuperclass = true;
  if (hasSameSuperclass && child.widget == newWidget) { / / first place
   if(child.slot ! = newSlot) updateSlotForChild(child, newSlot); newChild = child; }return newChild;
}
Copy the code
If the old and new widgets are the same Widget. In the display of the next frame, the position is changed and the position information is updated.
Case analysis
The body code and functions of the page remain the same 👉 detailed code, and compared with case 2, StatefulColor has added the Key attribute.
Phenomenally, the blocks are swapped.
Remember what we said earlier about possible causes?
Element’s widget points to a new widget, such as the pink widget Element used to point to. Now it points to a yellow widget.
Two: rebuild the Element based on the swapped Widget. For example, destroy the Element that originally hosted the red Widget and generate an Element based on the yellow Widget.
Three: Exchange Element directly. For example, swap the red and yellow Element positions
Case one is interchangeable because of the first reason.
So let’s take a look at which of these cases case three falls into.
Similar to the analysis above, the updateChildren method is executed.
Unlike cases 1 and 2, the updateChildren method code goes to —– to store reusable elements.
Step 1 and step 2 are skipped because the key is different each time the Widget is diff. For example, the key of the Element Widget in the first position is key1, and the key of the Widget to be displayed is key2
Here’s what the Map looks like after step 3:
Key1 :Element1 (Widget is 1) Key2 :Element2 (Widget is 2)Copy the code
Follow the process: Widget2’s key is key2, find key2 in the Map, fetch Element2. But the previous node of Element2 was Element1, and now Element2 is displayed in the first location, so its slot is null
After the third step is processed, start the gradual update. Now, where Widget1 was displayed, Widget2 is displayed.
So:
final ElementNewChild = updateChild(oldChild, newWidget, previousChild); OldChild: is Element2, and the widget of Element2 is Widget2 newWidget: is Widget2 previousChild:null-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- oldChild: Is Element1, and the widget of Element1 is Widget1 newWidget: is Widget1 previousChild: Element2Copy the code
The updateChild method is also implemented in the third part of the updateChild code, but the new and old widgets are installed in the same way, but the slot information is different, so the position of the Element is swapped, and the State is swapped. That’s why the colors switch.
The update process is as follows:
Note that State’s build method is not executed in this case either.
summary
The StatefulWidget color is changed after the Key is set:
- Element is reused, and its Widget and State information are retained
- Updated Element location information
Four cases
We wrap the StatefulWidget with a Padding, and the code case is in the SwapColorDemo4 class. But when we click the button, the color block appears randomly.
In view of the phenomenon, the graph blocks are randomly exchanged. Compared to case three, it’s just a layer. After the previous analysis, let’s first consider the question, under what circumstances do colors appear randomly?
We know that the color is in the State, so the Element that bears the State has been recreated.
So when will it be recreated? When it can’t be reused.
Next, let’s look at the survival of the Element in the StatefulWidget.
When you’re dealing with the Padding layer, the process is similar to case 1. It just stays in the first step of the loop —– diff down and updates the child nodes
Because the runtimeType of the old and new widgets is Padding, and because we did not set the Key manually, the Key property of the old and new widgets is null. So canUpdate returns true. The framework thinks it needs to be reused, so it updates the child.
final Element newChild = updateChild(oldChild, newWidget, previousChild); The oldChild is an Element of the old Padding and the newWidget is the new PaddingCopy the code
Execute the Padding update method, and forget about the rest. When displaying the Padding child, the Key of the child is different from that of the other child, so the State of the color is reconstructed as well.
The update process is as follows:
summary
The StatefulWidget color appears randomly after wrapping the Padding:
- The Padding Element is reused, but since the Padding child has a Key, the Padding child node is not reused.
conclusion
With these few examples, we know that Flutter is 99% of the update logic, and the remaining 1% is InheritedWidget, which we’ll follow with music and dance.