飞道的博客

【Flutter入门】setState是如何影响Widget build的

365人阅读  评论(0)

在Flutter中,一切为Widget。根据Widget是否可以动态更新,引入了State状态的概念。即StatelessWidget、StatefulWidget。

一、State生命周期

State是StatefulWidget内部持有的用来维护该Widget状态的对象。由StatefulWidget的createState()方法进行实例化。

二、setState()执行流程

在Flutter中,Widget是不可变,要想刷新界面,得调用StatefulWidget中State对象的setState()方法。最终由Flutter的framework再重新生成一个Widget树。
而在Android中,View只创建一次,当要对view进行重绘时,调用invalid()方法。

setState()方法传入的是一个无参回调,它将通知framework,当前Widget内部的状态发生了改变,请求重新渲染。
在setState()方法里面,会进行以下判断:

 @protected
  void setState(VoidCallback fn) {
    assert(fn != null);//1.回调不能为空
    assert(() {
    //2.如果生命周期为defunct,说明该widget的状态已经dispose,
    //抛出异常
      if (_debugLifecycleState == _StateLifecycle.defunct) {
       ......
      }
      //3.如果State创建成功,但没有挂载成功(即与当前Widget的上下文关联起来),抛出异常
      if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
      ......
      }
      return true;
    }());
    //4.setState()里面的回调只能是同步执行,不能是异步操作
    //因此如果是Future的,抛出异常。
    final dynamic result = fn() as dynamic;
    assert(() {
      if (result is Future) {
       ......
    }());
    //5.将当前widget元素标记为需要rebuild.
    _element.markNeedsBuild();
  }
$2.1、markNeedsBuild

在Flutter中,Widget与Android中的View不同,Widget是不可变的,只能由内部的State对象来维护状态,最后通知framework来对widget进行重绘(重新构建)。因此在这里,系统引入了一个dirty(脏)的概念。

/// Returns true if the element has been marked as needing rebuilding.
  bool get dirty => _dirty;
  bool _dirty = true;

而同时,在每一次的build()过后,系统都会将它置为false,为了方便下一次的状态更新,发起重建。

 /// [rebuild] when the element needs updating.
  @override
  void performRebuild() {
    ......
    Widget built;
    try {
      built = build();
     .....
    } catch (e, stack) {
     ......
    } finally {
      // We delay marking the element as clean until after calling build() so
      // that attempts to markNeedsBuild() during build() will be ignored.
      _dirty = false;
      .......
    }

因此在markNeedsBuild()方法内部也做了多重判断,来确保当前需要rebuild的元素,已经被标记为dirty。只有dirty为true,widget树才会生效(就是确保只有一份有效的widget树)。

void markNeedsBuild() {
//1.确保当前元素的状态生命周期是有效的,没有dipose
    assert(_debugLifecycleState != _ElementLifecycle.defunct);
    if (!_active)
      return;
    assert(owner != null);
    assert(_debugLifecycleState == _ElementLifecycle.active);
    ......
    if (dirty)
      return;
      ///2.将当前元素dirty标记为true,加入到全局的脏链中
    _dirty = true;
    ///3.准备为当前元素调度build
    //owner:为BuildOwner,主要是用于跟踪哪些widget需要重新构建(rebuild)
    owner.scheduleBuildFor(this);
  }
$2.2、scheduleBuildFor

属于BuildOwner类中的方法,接收Element类型参数。主要作用是:将当前Element添加到脏链中,以便在WidgetsBinding.drawFrame调用buildScope,能遍历脏链中的Element,进行重建。

 void scheduleBuildFor(Element element) {
 //1.判断当前element是否不为null以及dirty是否为true等
  ....
  //2.onBuildScheduled回调不为null,开始回调
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
     ....
     //onBuildScheduled为回调函数,在BuildOwner实例化时传入
     //而BuildOwner的实例化在WidgetsBinding初始化中完成
      onBuildScheduled();
    }
    //同时将当前element加入到脏链中
    _dirtyElements.add(element);
    element._inDirtyList = true;
   ......
  }
$2.3、onBuildScheduled

onBuildScheduled为回调函数,在BuildOwner实例化时传入,而BuildOwner的实例化在WidgetsBinding初始化中完成。

// The glue between the widgets layer and the Flutter engine.
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _buildOwner = BuildOwner();
    //1.将_handleBuildScheduled方法传递给onBuildScheduled
    buildOwner.onBuildScheduled = _handleBuildScheduled;
void _handleBuildScheduled() {
    // If we're in the process of building dirty elements, then changes
    // should not trigger a new frame.
    .......
    //2.其实这一切都是确保能准确的在下一帧中得到渲染。
    ensureVisualUpdate();
  }

跟Android屏幕刷新机制一样(我之前有写过一篇关于Android屏幕刷新机制的博文),也是由垂直信号来控制渲染。

$2.4、ensureVisualUpdate
 void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        ///1.用于调度新帧
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }


void scheduleFrame() {
    ....
    ///1.确保帧回调已被注册
    ///当Vysnc到达时,只有被注册的回调才会被调用,整个调用链才会生效,
    ///最终实现widget tree的rebuild.
    ensureFrameCallbacksRegistered();
    ///2.由window安排下一帧,这里与Flutter底层交互
    //void scheduleFrame() native 'Window_scheduleFrame';
    window.scheduleFrame();
    _hasScheduledFrame = true;
  }

转载:https://blog.csdn.net/u011153817/article/details/105902801
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场