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
相关推荐
孤鸿玉9 小时前
Fluter InteractiveViewer 与ScrollView滑动冲突问题解决
flutter
叽哥16 小时前
Flutter Riverpod上手指南
android·flutter·ios
BG1 天前
Flutter 简仿Excel表格组件介绍
flutter
zhangmeng1 天前
FlutterBoost在iOS26真机运行崩溃问题
flutter·app·swift
恋猫de小郭1 天前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程
卡尔特斯1 天前
Flutter A GlobalKey was used multipletimes inside one widget'schild list.The ...
flutter
w_y_fan2 天前
Flutter 滚动组件总结
前端·flutter
醉过才知酒浓2 天前
Flutter Getx 的页面传参
flutter
火柴就是我3 天前
flutter 之真手势冲突处理
android·flutter
Speed1233 天前
`mockito` 的核心“打桩”规则
flutter·dart