美文网首页@IT·互联网
Flutter 的 StateFullWidget调用SetSt

Flutter 的 StateFullWidget调用SetSt

作者: 黑炭长 | 来源:发表于2024-03-19 17:52 被阅读0次

    SetState方法

    _element!.markNeedsBuild();
    

    除去一些判断外 最终是调用了_element!.markNeedsBuild();

    if (_lifecycleState != _ElementLifecycle.active) {
          return;
        }
    if (dirty) {
          return;
        }
        _dirty = true;
        owner!.scheduleBuildFor(this);
    

    防止重复刷新 这里判断了 _lifecycleState != _ElementLifecycle.active 该值在mount方法中设置为active,owner也是在mount中赋值的
    同时若是当亲已被标记dirty,也不刷新
    接下来是owner!.scheduleBuildFor(this)

    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
          _scheduledFlushDirtyElements = true;
          onBuildScheduled!();
        }
        _dirtyElements.add(element);
        element._inDirtyList = true;
    

    这里调用onBuildScheduled进行回调状态
    同时将element加入dirty列表,并做标记
    onBuildScheduled!();

    mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
      @override
      void initInstances() {
        super.initInstances();
        _instance = this;
        // 这里
        buildOwner.onBuildScheduled = _handleBuildScheduled;
        window.onLocaleChanged = handleLocaleChanged;window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
     SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    SystemChannels.system.setMessageHandler(_handleSystemMessage);  FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
      }
    }
    

    可以看出,在WidgetsBinding初始化的时候就进行了绑定,最终调用的是_handleBuildScheduled

    void _handleBuildScheduled() {
        //调用ensureVisualUpdate
        ensureVisualUpdate();
      }
    
    

    ensureVisualUpdate()

    void ensureVisualUpdate() {
        switch (schedulerPhase) {
          case SchedulerPhase.idle:
          case SchedulerPhase.postFrameCallbacks:
            scheduleFrame();
            return;
          case SchedulerPhase.transientCallbacks:
          case SchedulerPhase.midFrameMicrotasks:
          case SchedulerPhase.persistentCallbacks:
            return;
        }
      }
    

    SchedulerPhase.postFrameCallbacks


    截屏2024-03-20 13.54.19.png

    这里我们主要看 postFrameCallbacks 执行下一帧的渲染工作

    scheduleFrame() {
    
      //调用Window 的scheduleFrame方法是一个 native 方法
      window.scheduleFrame();  
    
    }
    

    WidgetsBinding 混入的这些 Binding 中基本都是监听并处理 Window 对象的一些事件,然后将这些事件按照 Framework 的模型包装、抽象然后分发。可以看到 WidgetsBinding 正是粘连 Flutter engine 与上层 Framework 的“胶水”。

    截屏2024-03-20 14.28.43.png
    window.scheduleFrame
    /// 注册回调
    ensureFrameCallbacksRegistered();
    /// 向引擎发送消息
        platformDispatcher.scheduleFrame();
    

    接着是绘制回调

    @override
    void drawFrame() {
     ...
      try {
        if (renderViewElement != null)
          // 重构需要更新的element
          buildOwner.buildScope(renderViewElement);
        super.drawFrame(); //调用RendererBinding的drawFrame()方法
        buildOwner.finalizeTree();
      }
    }
    

    buildOwner.buildScope(renderViewElement);

    void buildScope(Element context, [ VoidCallback callback ]) {
        ...
          while (index < dirtyCount) {
            assert(_dirtyElements[index] != null);
            assert(_dirtyElements[index]._inDirtyList);
            assert(!_dirtyElements[index]._active || _dirtyElements[index]._debugIsInScope(context));
            try {
              //while 循环进行元素重构
              _dirtyElements[index].rebuild();
            } catch (e, stack) {
            ...
            }
          }
    
    for (final Element element in _dirtyElements) {
            assert(element._inDirtyList);
            element._inDirtyList = false;
          }
          _dirtyElements.clear();
          _scheduledFlushDirtyElements = false;
          _dirtyElementsNeedsResorting = null;
          if (!kReleaseMode) {
            FlutterTimeline.finishSync();
          }
      }
    

    _dirtyElements[index].rebuild() 元素重构,重构后 将dirtyelement _inDirtyList = false 将dirtyelement数组清空

    performRebuild() {
    
    _dirty = false;
      if (_newWidget != null) {
          // _newWidget can be null if, for instance, we were rebuilt
          // due to a reassemble.
          final Widget newWidget = _newWidget!;
          _newWidget = null;
          update(newWidget as RenderObjectToWidgetAdapter<T>);
        }  
    
    }
    

    performRebuild 将_dirty置为false
    update(newWidget as RenderObjectToWidgetAdapter<T>); 更新widget

    void _rebuild() {
        try {
          _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot);
        } catch (exception, stack) {
          final FlutterErrorDetails details = FlutterErrorDetails(
            exception: exception,
            stack: stack,
            library: 'widgets library',
            context: ErrorDescription('attaching to the render tree'),
          );
          FlutterError.reportError(details);
          final Widget error = ErrorWidget.builder(details);
          _child = updateChild(null, error, _rootChildSlot);
        }
      }
    

    更新child

    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;
          // When the type of a widget is changed between Stateful and Stateless via
          // hot reload, the element tree will end up in a partially invalid state.
          // That is, if the widget was a StatefulWidget and is now a StatelessWidget,
          // then the element tree currently contains a StatefulElement that is incorrectly
          // referencing a StatelessWidget (and likewise with StatelessElement).
          //
          // To avoid crashing due to type errors, we need to gently guide the invalid
          // element out of the tree. To do so, we ensure that the `hasSameSuperclass` condition
          // returns false which prevents us from trying to update the existing element
          // incorrectly.
          //
          // For the case where the widget becomes Stateful, we also need to avoid
          // accessing `StatelessElement.widget` as the cast on the getter will
          // cause a type error to be thrown. Here we avoid that by short-circuiting
          // the `Widget.canUpdate` check once `hasSameSuperclass` is false.
          assert(() {
            final int oldElementClass = Element._debugConcreteSubtype(child);
            final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);
            hasSameSuperclass = oldElementClass == newWidgetClass;
            return true;
          }());
          if (hasSameSuperclass && child.widget == newWidget) {
            // We don't insert a timeline event here, because otherwise it's
            // confusing that widgets that "don't update" (because they didn't
            // change) get "charged" on the timeline.
            if (child.slot != newSlot) {
              updateSlotForChild(child, newSlot);
            }
            newChild = child;
          } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
            if (child.slot != newSlot) {
              updateSlotForChild(child, newSlot);
            }
            final bool isTimelineTracked = !kReleaseMode && _isProfileBuildsEnabledFor(newWidget);
            if (isTimelineTracked) {
              Map<String, String>? debugTimelineArguments;
              assert(() {
                if (kDebugMode && debugEnhanceBuildTimelineArguments) {
                  debugTimelineArguments = newWidget.toDiagnosticsNode().toTimelineArguments();
                }
                return true;
              }());
              FlutterTimeline.startSync(
                '${newWidget.runtimeType}',
                arguments: debugTimelineArguments,
              );
            }
            child.update(newWidget);
            if (isTimelineTracked) {
              FlutterTimeline.finishSync();
            }
            assert(child.widget == newWidget);
            assert(() {
              child.owner!._debugElementWasRebuilt(child);
              return true;
            }());
            newChild = child;
          } else {
            deactivateChild(child);
            assert(child._parent == null);
            // The [debugProfileBuildsEnabled] code for this branch is inside
            // [inflateWidget], since some [Element]s call [inflateWidget] directly
            // instead of going through [updateChild].
            newChild = inflateWidget(newWidget, newSlot);
          }
        } else {
          // The [debugProfileBuildsEnabled] code for this branch is inside
          // [inflateWidget], since some [Element]s call [inflateWidget] directly
          // instead of going through [updateChild].
          newChild = inflateWidget(newWidget, newSlot);
        }
    
        assert(() {
          if (child != null) {
            _debugRemoveGlobalKeyReservation(child);
          }
          final Key? key = newWidget.key;
          if (key is GlobalKey) {
            assert(owner != null);
            owner!._debugReserveGlobalKeyFor(this, newChild, key);
          }
          return true;
        }());
    
        return newChild;
      }
    
    • 新旧widget有同样的父类且widget相同,则只更新slot(由parent设置,用于确定位于parent中的位置)
    • 父类相同,且Widget.canUpdate返回true,则更新element对应的widget配置
    • 否则,将原element置为deactate,使用inflateWidget创建新的element

    inflateWidget 中执行element的mount方法,更新全局的globalKey,更新依赖,更新notification,同时执行rebuild

    至此更新完毕

    常用容器widget的生命周期

    • StatelessWidget生命周期
      build 每次界面刷新的时候都会调用
    • StatefulWidget生命周期
      createState - 创建的时候调用 且只调用一次
      initState - 创建后调用的第一个方法 类似iOS的viewdidload,此时mount变为true,直到disponse才变为false
      didChangeDependencies - 第一次创建的时候 会在initstate后立即调用,当刷新的时候就不调用了,除非widget依赖的 ingeritrWidget发生变化,所以 didChangeDependencies 可能会多次调用
      build - 创建后会在调用didChangeDependencies 后立即调用
      重新渲染时,也会调用
      addPostFrameCallback - 添加当前帧的绘制回调,只调用一次,我们一般会在initstate中添加当前帧的回调
    import 'package:flutter/scheduler.dart';
    @override
    void initState() {super.initState();SchedulerBinding.instance.addPostFrameCallback((_) => {});
    } 
    
    

    addPersistentFrameCallback - 实时frame绘制回调,这个函数会在每次绘制frame结束后调用,可以用作FPS的检测

    didUpdateWidget - 是否复用widget时才会调用
    deactivate - 当前widgetkey或是类型发生变化,需要重新创建element,将原state从移除调用
    dispose - widget不需要再显示,从渲染树中移除,state永久移除,会调用该方法,可以在该方法中取消监听,动画的操作

    相关文章

      网友评论

        本文标题:Flutter 的 StateFullWidget调用SetSt

        本文链接:https://www.haomeiwen.com/subject/jaqttjtx.html