Flutter 页面改写机制

StateFullWidget页面改写

updateChild

Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    if (newWidget == null) {
      if (child != null) {
        deactivateChild(child);
      }
      return null;
    }
    final Element newChild;
    if (child != null) {
      bool hasSameSuperclass = true;
      if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        newChild = child;
      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        child.update(newWidget);
        newChild = child;
      } else {
        deactivateChild(child);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
      newChild = inflateWidget(newWidget, newSlot);
    }
    final Key? key = newWidget.key;
    if (key is GlobalKey) {
      owner!._debugReserveGlobalKeyFor(this, newChild, key);
    }
    return newChild;
  }

这段Flutter源码是关于Element类中的updateChild办法。Element是Flutter结构中用于构建和办理Widget树的基本元素:

以下是对该办法履行流程的扼要分析:

参数检查: 办法接受三个参数,child表明当时的子元素(能够为null),newWidget表明新的Widget,newSlot表明新的槽(slot)。

处理newWidget为null的状况: 假如newWidget为null,表明要移除当时的子元素,因此会调用deactivateChild(child)来停用当时的子元素,并回来null。

处理newWidget不为null的状况:

  • a. 假如当时child不为null,表明存在已有的子元素。

  • b. 检查是否具有相同的父类(hasSameSuperclass),假如有,则进行进一步的比较。

  • c. 假如child.widget与newWidget相同,且child.slot与newSlot不同,则更新child的槽。

  • d. 假如child.widget与newWidget相同且槽相同,直接回来child。

  • e. 假如具有相同的父类且Widget.canUpdate(child.widget, newWidget)为true,则表明能够直接更新child的Widget。更新槽并回来child。

canUpdate 办法实现为:

  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
  • f. 假如以上条件都不满足,调用deactivateChild(child)来停用当时子元素。

  • g. 调用 inflateWidget(newWidget, newSlot)来创立并回来新的子元素。

处理child为null的状况: 假如child为null,表明当时没有子元素,直接调用inflateWidget(newWidget, newSlot)来创立并回来新的子元素。

回来结果: 回来新创立的子元素newChild。

这段代码首要负责办理Element的子元素,依据新的Widget和槽来更新或创立子元素,并依据必定的条件判别是否需求停用当时子元素。

继续查看inflateWidget 函数:

  Element inflateWidget(Widget newWidget, Object? newSlot) {
    try {
      final Key? key = newWidget.key;
      if (key is GlobalKey) {
        final Element? newChild = _retakeInactiveElement(key, newWidget);
        if (newChild != null) {
          newChild._activateWithParent(this, newSlot);
          final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
          return updatedChild!;
        }
      }
      final Element newChild = newWidget.createElement();
      newChild.mount(this, newSlot);
      return newChild;
    } finally {
    }
  }

在看createElement办法

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ super.key });
  @override
  StatefulElement createElement() => StatefulElement(this);
}

在看下StatefulElement办法:

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
    state._element = this;
    state._widget = widget;
  }
}

回到inflateWidget办法

 Element inflateWidget(Widget newWidget, Object? newSlot) {
    try {
      final Key? key = newWidget.key;
      if (key is GlobalKey) {
        final Element? newChild = _retakeInactiveElement(key, newWidget);
        if (newChild != null) {
          newChild._activateWithParent(this, newSlot);
          final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
          return updatedChild!;
        }
      }
      final Element newChild = newWidget.createElement();
      newChild.mount(this, newSlot);
      return newChild;
    } finally {
    }
  }

看一下 newChild.mount(this, newSlot); 的履行:


abstract class ComponentElement extends Element {
  void mount(Element? parent, Object? newSlot) {
      _firstBuild();
    }
}

在看下 _firstBuild办法:


class StatefulElement extends ComponentElement {
  void _firstBuild() {
      try {
        final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
      } finally {
      }
      state.didChangeDependencies();
      super._firstBuild();
    }
  }
}

在这里调用了 state.initState()

StateFullWidget inflateWidget 总结

所以widget改写中, 在inflateWidget中创立新的element ,全体的履行次序如下:

  • createElement
  • createState
  • initState
  • didChangeDependencies
  • build

hasSameSuperClass didUpdateWidget改写

假如statefullwidget key没变,则调用次序如下:

回到流程:

Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
    if (newWidget == null) {
      if (child != null) {
        deactivateChild(child);
      }
      return null;
    }
    final Element newChild;
    if (child != null) {
      bool hasSameSuperclass = true;
      if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        newChild = child;
      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        child.update(newWidget);
        newChild = child;
      } else {
        deactivateChild(child);
        newChild = inflateWidget(newWidget, newSlot);
      }
    } else {
      newChild = inflateWidget(newWidget, newSlot);
    }
    final Key? key = newWidget.key;
    if (key is GlobalKey) {
      owner!._debugReserveGlobalKeyFor(this, newChild, key);
    }
    return newChild;
  }

看下 child.update(newWidget);

class StatefulElement extends ComponentElement {
  void update(StatefulWidget newWidget) {
    super.update(newWidget);
    assert(widget == newWidget);
    final StatefulWidget oldWidget = state._widget!;
    _dirty = true;
    state._widget = widget as StatefulWidget;
    try {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(true);
      final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
    } finally {
      _debugSetAllowIgnoredCallsToMarkNeedsBuild(false);
    }
    rebuild();
  }
}

在这一步调用的state.didUpdateWidget(oldWidget)

rebuild();办法如下

Element rebuild

abstract class Element extends DiagnosticableTree implements BuildContext {
 void rebuild() {
    performRebuild();
  }
}

ComponentElement performRebuild


abstract class ComponentElement extends Element {
  void performRebuild() {
    Widget? built;
    try {
      built = build();
    }
    try {
      _child = updateChild(_child, built, slot);
    } catch (e, stack) {
      _child = updateChild(null, built, slot);
    }
  }
}

StatefulElement build

  class StatefulElement extends ComponentElement {
    Widget build() => state.build(this);
  }

假如statefullwidget didUpdateWidget总结

假如statefullwidget key没变,则调用次序如下:

  • didUpdateWidget
  • build

总结

  1. StatefulWidget:

    • StatefulWidget是一个有状况的Widget,它能够包含随时间变化的数据(状况)。
    • 通过调用setState办法,能够告诉Flutter结构需求从头构建UI,以便更新状况。
  2. State:

    • StatefulWidget的状况被封装在对应的State目标中。
    • State目标包含与UI交互的逻辑,而且能够在initStatedidUpdateWidget等生命周期办法中进行恰当的处理。
  3. hasSameSuperclass:

    • 在你flutter的源代码中,有一个hasSameSuperclass的变量,这用于检查两个Widget是否有相同的父类。
    • 假如两个Widget具有相同的父类,可能会影响到是否能够直接复用现有的Element目标。
  4. Key:

    • 在Flutter中,Key是用于标识Widget的目标,能够协助Flutter结构更好地办理Widget树的更新。
    • 假如两个Widget具有相同的Key,Flutter可能会以为它们表明相同的逻辑实体,并测验进行更新而不是毁掉和重建。

StatefulWidget更新时,结构会测验智能地更新现有的Element目标,而不是毁掉并从头构建整个Widget树。关键因素包括:

  • Key的使用: 假如Widget具有Key,结构会测验匹配新旧Widget的Key,以确定是否能够复用现有的Element目标。
  • State的生命周期办法: State目标的生命周期办法,如initStatedidUpdateWidget,能够用于履行恰当的逻辑,以呼应Widget的更新。
  • 相同Widget类型的比较: 在你供给的代码中,有一段比较child.widget == newWidget的逻辑,这可能涉及到判别是否是相同类型的Widget。
  1. hasSameSuperclass严重效果

    • 假如statefullwidget key没变,则调用次序如下:

      • didUpdateWidget
      • build
    • runtimetype相同可是key产生修正,在inflateWidget中创立新的element ,全体的履行次序如下:

      • createElement
      • createState
      • initState
      • didChangeDependencies
      • build