At the beginning of this year, I began to learn to use Flutter, but I did not have a thorough understanding of setState(), the relationship between element and widget. After a period of experience, I got some feelings from reading the big man’s article, so I wrote my own learning summary to avoid forgetting and may not be correct
The following are two articles of the big guy. It is suggested to read several times first. Of course, there are many articles of other big guys
I’ve been using setState() incorrectly
The interviewer asked me about the life cycle of State. What should I say
About the rendering process of Flutter, I have already explained that setState() has been wrongly used, which is enough for me. But in the process of reading the article on Element, ComponentElement, StatelessElement, StatefulElement various functions to cover a little confused, after looking at the source code according to their own understanding to do the next comb
Element
inflateWidget
Element inflateWidget(Widget newWidget, dynamic newSlot){
final Element newChild = newWidget.createElement();
newChild.mount(this, newSlot);
return newChild;
}
Copy the code
Create a new child element from a child widget and mount the child element into the Element tree, as explained in the source code comments. This method is usually called by [updateChild], but can be called directly by subclasses that need more fine-grained control over the creation of elements. When updateChild is called, the variable is named newChild because we call inflateWidget() in updateChild to return the parent element.
mount
@mustCallSuper void mount(Element parent, dynamic newSlot) { _parent = parent; _slot = newSlot; _depth = _parent ! = null ? _parent.depth + 1 : 1; _active = true; _updateInheritance(); }Copy the code
The _updateInheritance method, as the name implies, is associated with the inheritWidget. Notice that the parent function parameter is this of the inflateWidget. This also confirms that inflateWidget is usually called by its parent, element.updatechild
updateChild
@protected Element updateChild(Element child, Widget newWidget, dynamic newSlot) { if (newWidget == null) { if (child ! = null) //child == null && newWidget == null deactivateChild(child); //child ! = null && newWidget == null return null; } if (child ! = null) { if (child.widget == newWidget) { //child ! = null && newWidget == child.widget if (child.slot ! = newSlot) updateSlotForChild(child, newSlot); return child; } if (Widget.canUpdate(child.widget, newWidget)) { if (child.slot ! = newSlot) updateSlotForChild(child, newSlot); //child ! = null && Widget.canUpdate(child.widget, newWidget) child.update(newWidget); return child; } deactivateChild(child); } // child ! = null && ! Widget.canUpdate(child.widget, newWidget) return inflateWidget(newWidget, newSlot); }Copy the code
This method is the core logic, if you don’t understand the above code please read this article carefully because I’ve been using setState() incorrectly
- If the previous position child is null
- If the newWidget is null, the location will always have no child nodes, so return NULL.
- If the newWidget is not null, the location has a new child node called inflateWidget(newWidget, newSlot) to generate a new Element return
- If the previous child is not null
- If the newWidget is null, the previous node needs to be removed from the location. Call deactivateChild(Child) to remove it and return NULL
- If the newWidget is not null, call widget. canUpdate(child. Widget, newWidget) first to see if it can be updated. This method compares the runtimeType and key of the two widgets. If the runtimeType and key of the two widgets are the same, the child widgets have not changed, but need to update the current node data child.update(newWidget) according to the configuration list. Return inflateWidget(newWidget, newSlot) after deactivateChild(Child)
update
@mustCallSuper
void update(covariant Widget newWidget) {
_widget = newWidget;
}
Copy the code
The abstract method is very simple. It simply means that a child element updates its widget, which is equivalent to reusing a child element and reassigning a child widget
Rebuild and performRebuild
void rebuild() {
performRebuild();
}
@protected
void performRebuild();
Copy the code
These two methods are much simpler:…. to override for subclasses
doubt
With that in mind, a few new questions arise
updateChild
When do you call itElement
There is no _child variable,updateChild
And then who gets the element- If A’s child is B, A passes
updateChild
After generating B, how does B generate ITS own child
Above these problems can be found in the source of the answer, ComponentElement, StatelessElement, StatefulElement these subclasses to rewrite the above method, our questions can be solved in the subclass
ComponentElement
mount
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_firstBuild();
}
Copy the code
Here’s a new method
_firstBuild
void _firstBuild() {
rebuild();
}
Copy the code
See rebuild() is called again; In the previous article, the buildOwner. BuildScope (renderViewElement) method iterates through the dirtyElement array with _dirtyElements[index].rebuild()
performRebuild
@override
void performRebuild() {
Widget built;
built = build();
_child = updateChild(_child, built, slot);
}
Copy the code
PerformRebuild is empty in Element. ComponentElement overrides the method and creates the Element _Child variable. In updateChild, the inflateWidget generates new child Elements and assigns values to _Child
So firstBuild, as the name suggests, starts rebuilding the first time you create a mount element and just like the name suggests it starts rebuilding the first time you refresh the interface so in ComponentElement, it’s performRebuild, no different, Stateless is handled differently than Stateful
StatelessElement
update
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
Copy the code
StatelessElement just overwrites update, calls rebuild, and then performRebuild and so on
If StatelessWidget A child StatelessWidget B then StatelessElement Aelement Belement is called in the order when it is first loaded
parent.inflateWidget => A.createElement => Aelement.mount => Aelement.firstBuild => Aelement.rebuild => Aelement.performRebuild => aelement. updateChild => Belement creates the rebuild as _child is empty
When aelement. markneedBuild is set to aelement. rebuild, Belement already exists, and aelement. _child is not empty. CanUpdate ==true _child.update => _child.rebuild
Look at the picture below to make a better impression
Answer the questions
updateChild
When do you call it
rebuild
Is called whenperformRebuild
, and then callupdateChild
.rebuild
When is it called? Is in thefirstBuild
And system refresh callback dirty array traversal dirty array callrebuild
Element
There is no _child variable,updateChild
And then who gets the element
- Definition in subclasses
_child
.updateChild
Assigned to _child
- If A’s child is B, how does B generate its own child after A generates B using updateChild
update
In the methodrebuild
His updatechild