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

  1. updateChildWhen do you call it
  2. ElementThere is no _child variable,updateChildAnd then who gets the element
  3. If A’s child is B, A passesupdateChildAfter 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
  1. updateChildWhen do you call it
  • rebuildIs called whenperformRebuild, and then callupdateChild.rebuildWhen is it called? Is in thefirstBuildAnd system refresh callback dirty array traversal dirty array callrebuild
  1. ElementThere is no _child variable,updateChildAnd then who gets the element
  • Definition in subclasses_child.updateChildAssigned to _child
  1. If A’s child is B, how does B generate its own child after A generates B using updateChild
  • updateIn the methodrebuildHis updatechild