flutter StatefullWidget组件State状态管理和更新机制

Flutter 页面刷新机制

StateFullWidget页面刷新

updateChild

ini 复制代码
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 方法实现为:

ini 复制代码
  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 函数:

dart 复制代码
  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方法

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

}

在看下StatefulElement方法:

scala 复制代码
class StatefulElement extends ComponentElement {

  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {

    state._element = this;

    state._widget = widget;
 
  }

}

回到inflateWidget方法

dart 复制代码
 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); 的执行:

scala 复制代码
abstract class ComponentElement extends Element {

  void mount(Element? parent, Object? newSlot) {

   

      _firstBuild();

    }



}

在看下 _firstBuild方法:

scala 复制代码
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没变,则调用顺序如下:

回到流程:

ini 复制代码
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);

ini 复制代码
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

scala 复制代码
abstract class Element extends DiagnosticableTree implements BuildContext {
 void rebuild() {

 
    performRebuild();

  }

}

ComponentElement performRebuild

scala 复制代码
  
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

scala 复制代码
  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
相关推荐
AiFlutter14 小时前
Flutter之Package教程
flutter
Mingyueyixi18 小时前
Flutter Spacer引发的The ParentDataWidget Expanded(flex: 1) 惨案
前端·flutter
crasowas1 天前
Flutter问题记录 - 适配Xcode 16和iOS 18
flutter·ios·xcode
老田低代码2 天前
Dart自从引入null check后写Flutter App总有一种难受的感觉
前端·flutter
AiFlutter2 天前
Flutter Web首次加载时添加动画
前端·flutter
ZemanZhang4 天前
Flutter启动无法运行热重载
flutter
AiFlutter4 天前
Flutter-底部选择弹窗(showModalBottomSheet)
flutter
帅次5 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
程序者王大川5 天前
【前端】Flutter vs uni-app:性能对比分析
前端·flutter·uni-app·安卓·全栈·性能分析·原生
yang2952423615 天前
使用 Vue.js 将数据对象的值放入另一个数据对象中
前端·vue.js·flutter