The Widget is actually an Element configuration item. The real class in Flutter that represents the Element displayed on the screen is Element.

Element itself does not deal with laying out, painting, and hit testing operations, which are implemented by RenderObject

Element life cycle

Initial

Element is not normally called directly, but is initialized by calling widget.createElement. Read the Widget source code for Element createElement(), We can see that the createElement method returns an Elment object.

The state of the Element is initial after createElement is created.

_ElementLifecycle _debugLifecycleState = _ElementLifecycle.initial;
Copy the code

Tips: _debugLifecycleState Internal property used to determine the current Element state.

Active

When RenderObject calls the mount method, a new Element is added to the Parent’s Render tree:

The RenderObject mount method first executes the Element mount method. Assert (() {_debugLifecycleState = _ElementLifecycle. Active; return true; } ()); , we can see that the state of Element has been changed to Active.

Elment mounts only life-cycle changes because Element does not do layout itself. Where does layout go? RenderObjectElement (RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement, RenderObjectElement

@mustCallSuper void mount(Element parent, dynamic newSlot) { _parent = parent; _slot = newSlot; _depth = _parent ! = null ? _parent.depth + 1 : 1; _active =true;
  if(parent ! = null) // Only assign ownershipif the parent is non-null
    _owner = parent.owner;
  if (widget.key is GlobalKey) {
    final GlobalKey key = widget.key;
    key._register(this);
  }
  _updateInheritance();
  assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; } ()); }Copy the code

Current status: Active and is displayed on the screen

Inactive

When Parent underway, the Widget will call the update method, and Flutter will determine whether the old and new widgets are the same according to the runtimeType and key properties. If they are the same, the element. update method will be called to update. If not, the old Element will be removed from the Render Tree, and the new Element will be filled in. (This part involves updating, inserting, and removing widgets, but this is just a sketchbook.) So can we manually change the runtimeType? Not so far. There’s a quote on the website,

If the parent wishes to change the runtimeType or key of the widget at this location in the tree, can do so by unmounting this element and inflating the new widget at this location.

To change the runtimeType, you must unstamp the element.

If the Ancestor element needs to remove the element from the tree, the Ancestor calls the deactivateChild method. This method removes the Element’s render object from the Render Tree. Push the element into the Inactive Elements list of the Ancestor. The element in the inactive Elements list will be called deactivate

Current state: Inactive and has been removed from the screen as if the element is still in memory

Defunct

When the animation ends, all inactive elements will be unmounted.

void unmount() { assert(_debugLifecycleState == _ElementLifecycle.inactive); assert(widget ! = null); assert(depth ! = null); assert(! _active);if (widget.key is GlobalKey) {
      final GlobalKey key = widget.key;
      key._unregister(this);
    }
    assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; } ()); }Copy the code

Current status: defunct

If the element is merged back into the tree before the last frame of the animation ends, the frame will remove it from inactive’s list, invoke the Element’s Activate method again, and push the Element’s Render object back into the Render Tree

void activate() { assert(_debugLifecycleState == _ElementLifecycle.inactive); assert(widget ! = null); assert(owner ! = null); assert(depth ! = null); assert(! _active); final bool hadDependencies = (_dependencies ! = null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies; _active =true;
    // We unregistered our dependencies in deactivate, but never cleared the list.
    // Since we're going to be reused, let's clear our list now. _dependencies? .clear(); _hadUnsatisfiedDependencies =false;
    _updateInheritance();
    assert(() { _debugLifecycleState = _ElementLifecycle.active; return true; } ());if (_dirty)
      owner.scheduleBuildFor(this);
    if (hadDependencies)
      didChangeDependencies();
}
Copy the code

use

Knowing Element’s life cycle, can we use that life cycle to implement special requirements in particular scenarios, such as component removal triggers?

RenderObjecgtElement or ComponentElement, because those two classes inherit from Element. Someone said, Is it okay if I inherit from Element? That’s fine, too. But as I said at the beginning, Element itself doesn’t handle laying out, painting, or hit testing, so you need to implement those yourself. Use RenderObjectElement or ComponentElement for layout rendering and so on.

We all use widgets to implement functions, and even if we inherit widgets, where does the Element in this article trigger? If we want to use this lifecycle, we need to create a new class that inherits from RenderObjectElment or ComponentElement. Trigger specific life cycles using methods such as mount, unmount, activate, etc.

The following code is a Demo I wrote

class MyWidget extends Widget {
  @override
  MyElement createElement() => MyElement(this);
}

class MyElement extends ComponentElement {
  MyElement(Widget widget) : super(widget);
  @override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    print('Trigger mount life cycle');
  }
  @override
  void deactivate() {
    super.deactivate();
    print('Trigger deactivate life cycle');
  }
  @override
  void unmount() {
    super.unmount();
    print('Trigger unmount life cycle');
  }
  @override
  Widget build() {
    // TODO: implement build
    return Text('test'); }}Copy the code

conclusion

This article covers some special nouns, such as Render Tree and RenderObject, which will be covered in detail in the next part if there is one.