Flutter笔记——State.setState发生了什么(源码学习)

终于放假啦,不用一直写业务代码了,也终于有点时间可以整理整理笔记啦。
我在这里先给大家拜个早年,恭祝大家新春快乐,吉祥安康啦!

拜年.gif

Flutter系列学习笔记

  • Flutter笔记——runApp发生了什么(源码学习)
  • Flutter笔记——State.setState发生了什么(源码学习)
  • 用Dart写的身份证号校验代码(可用于flutter项目)
  • HelloDart-构造函数特性
  • HelloDart-MixIn,土话记录多继承机制
  • Flutter笔记——MethodChannel(Native&Flutter数据交互)
  • Flutter笔记——FlutterActivity

1 State.setState

见该函数描述:

Notify the framework that the internal state of this object has changed.
Whenever you change the internal state of a State object, make the change in a function that you pass to setState.
通知Framework层:该State对象的内部状态发生了变化。无论你何时改变一个State对象的内部状态,请在传递给setState函数中做出修改。

在开发中我们通过控制setState对当前Widget做出修改,让UI元素重绘,过程见下面序列图

setState序列图.png

注释部分不清楚见下文:

  1. 断言传递函数不为null;
    当前state的状态判断,参见_StateLifecycle枚举和源码;
    传递的函数不能是future;
    最后调用_element.markNeedsBuild(),下一帧时当前State中Element需要重新build。
  2. 判断_debugLifecycleState的状态,参考_ElementLifecycle枚举和源码;
    当前Element的owner和active断言,active在deactivate函数被置为false,在mountedactive被置为true,所以调用setStae的时机需要注意;
    debug模式下的断言;
    如果当前Element._dirty已经为true的话返回;
    调用owner.scheduleBuildFor(this)函数。
  3. 将element加入到BuildOwner的dirty列表中,WidgetBinding在下一帧时会通过drawFrame绘制该element。
  4. element的_dirty标志位置为true,加入到dirty列表中,并且已经mount、active且未deactivate。dispose。下一帧该Element会通过RenderObject在其区域重绘。

2 源码学习

  1. State.setState(VoidCallback fn):
      @protected
      void setState(VoidCallback fn) {
        //断言fn不为空
        assert(fn != null);
        assert(() {
          //当前State的状态,不能等于_StateLifecycle.defunct,
          //该状态时,dispose函数已经调用
          if (_debugLifecycleState == _StateLifecycle.defunct) {
            throw FlutterError.fromParts([
              ErrorSummary('setState() called after dispose(): $this'),
              ErrorDescription(
                'This error happens if you call setState() on a State object for a widget that '
                'no longer appears in the widget tree (e.g., whose parent widget no longer '
                'includes the widget in its build). This error can occur when code calls '
                'setState() from a timer or an animation callback.'
              ),
              ErrorHint(
                'The preferred solution is '
                'to cancel the timer or stop listening to the animation in the dispose() '
                'callback. Another solution is to check the "mounted" property of this '
                'object before calling setState() to ensure the object is still in the '
                'tree.'
              ),
              ErrorHint(
                'This error might indicate a memory leak if setState() is being called '
                'because another object is retaining a reference to this State object '
                'after it has been removed from the tree. To avoid memory leaks, '
                'consider breaking the reference to this object during dispose().'
              ),
            ]);
          }
          //State即使创建了,也必须调用了mounted函数添加到树上
          if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
            throw FlutterError.fromParts([
              ErrorSummary('setState() called in constructor: $this'),
              ErrorHint(
                'This happens when you call setState() on a State object for a widget that '
                'hasn\'t been inserted into the widget tree yet. It is not necessary to call '
                'setState() in the constructor, since the state is already assumed to be dirty '
                'when it is initially created.'
              ),
            ]);
          }
          return true;
        }());
        //将fn转为dynamic动态类型
        final dynamic result = fn() as dynamic;
        assert(() {
          if (result is Future) {
            //如果result是一个Future,抛出异常。因为setState函数在下一帧就会重绘
            //Future函数是异步的,不能确定具体重绘时间
            throw FlutterError.fromParts([
              ErrorSummary('setState() callback argument returned a Future.'),
              ErrorDescription(
                'The setState() method on $this was called with a closure or method that '
                'returned a Future. Maybe it is marked as "async".'
              ),
              ErrorHint(
                'Instead of performing asynchronous work inside a call to setState(), first '
                'execute the work (without updating the widget state), and then synchronously '
               'update the state inside a call to setState().'
              ),
            ]);
          }
          // We ignore other types of return values so that you can do things like:
          //   setState(() => x = 3);
          return true;
        }());
        //调用State中的_elemnt.markNeedsBuild()函数
        _element.markNeedsBuild();
      }
    
  2. Element.markNeedsBuld():
      void markNeedsBuild() {
        //断言Element的当前状态不能等于defunct
        assert(_debugLifecycleState != _ElementLifecycle.defunct);
        //未处于active状态,
        if (!_active)
          return;
        //owner等于空断言异常
        assert(owner != null);
        //当前状态必须等于_ElementLifecycle.active
        assert(_debugLifecycleState == _ElementLifecycle.active);
        assert(() {
          //此Widget树是否出于构建阶段
          if (owner._debugBuilding) {
            //当前构建的目标不能等于null
            assert(owner._debugCurrentBuildTarget != null);
            //调用BuildOwner.lockState函数_debugStateLockLevel会增加,也就是
            //当前BuildOwner已经锁定State
            assert(owner._debugStateLocked);
            //判断当前构建的目标是否在构建域中
            if (_debugIsInScope(owner._debugCurrentBuildTarget))
              return true;
            //_debugAllowIgnoredCallsToMarkNeedsBuild该标志位位false时,
            //在State的initState、didUpdateWidget和build函数中调用setState函数都会报错。
            if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {
              final List information = [
                ErrorSummary('setState() or markNeedsBuild() called during build.'),
                ErrorDescription(
                  'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
                  'is already in the process of building widgets.  A widget can be marked as '
                  'needing to be built during the build phase only if one of its ancestors '
                  'is currently building. This exception is allowed because the framework '
                  'builds parent widgets before children, which means a dirty descendant '
                  'will always be built. Otherwise, the framework might not visit this '
                  'widget during this build phase.'
                ),
                describeElement(
                  'The widget on which setState() or markNeedsBuild() was called was',
                ),
              ];
              if (owner._debugCurrentBuildTarget != null)
                information.add(owner._debugCurrentBuildTarget.describeWidget('The widget which was currently being built when the offending call was made was'));
              throw FlutterError.fromParts(information);
            }
            assert(dirty); 
          } else if (owner._debugStateLocked) {
            //状态已经锁定,断言会报错
            assert(!_debugAllowIgnoredCallsToMarkNeedsBuild);
            throw FlutterError.fromParts([
              ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'),
              ErrorDescription(
                'This ${widget.runtimeType} widget cannot be marked as needing to build '
                'because the framework is locked.'
              ),
              describeElement('The widget on which setState() or markNeedsBuild() was called was'),
            ]);
          }
          return true;
        }());
        //如果该Element已经被标志位dirty,返回
        if (dirty)
          return;
        //当前Element的_dirty设置为true
        _dirty = true;
        //调用owner.scheduleBuildFor(Element)函数
        owner.scheduleBuildFor(this);
      }
    
  3. BuildOwner.scheduleBuildFor(Element):
      void scheduleBuildFor(Element element) {
        //Element不能为空
        assert(element != null);
        //element的BuildOwner对象必须等于当前对象
        assert(element.owner == this);
        assert(() {
          if (debugPrintScheduleBuildForStacks)
            debugPrintStack(label: 'scheduleBuildFor() called for $element${_dirtyElements.contains(element) ? " (ALREADY IN LIST)" : ""}');
          if (!element.dirty) {
            //当前Element不是dirty状态
            throw FlutterError.fromParts([
              ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.'),
              element.describeElement('The method was called for the following element'),
              ErrorDescription(
                'This element is not current marked as dirty. Make sure to set the dirty flag before '
                'calling scheduleBuildFor().'),
              ErrorHint(
                'If you did not attempt to call scheduleBuildFor() yourself, then this probably '
                'indicates a bug in the widgets framework. Please report it:\n'
                '  https://github.com/flutter/flutter/issues/new?template=BUG.md'
              ),
            ]);
          }
          return true;
        }());
        if (element._inDirtyList) {
          //element已经处于dirty脏列表中
          assert(() {
            if (debugPrintScheduleBuildForStacks)
              debugPrintStack(label: 'BuildOwner.scheduleBuildFor() called; _dirtyElementsNeedsResorting was $_dirtyElementsNeedsResorting (now true); dirty list is: $_dirtyElements');
            //_debugIsInBuildScope该值等于true时,才可以调用scheduleBuildFor函数
            if (!_debugIsInBuildScope) {
              throw FlutterError.fromParts([
                ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.'),
                ErrorHint(
                  'The BuildOwner.scheduleBuildFor() method should only be called while the '
                  'buildScope() method is actively rebuilding the widget tree.'
                ),
              ]);
            }
            return true;
          }());
          //需要排序Element树
          _dirtyElementsNeedsResorting = true;
          return;
        }
        //忽略
        if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
          _scheduledFlushDirtyElements = true;
          onBuildScheduled();
        }
        //将element添加到脏元素列表中
        _dirtyElements.add(element);
        //将element的_inDirtyList标记为true
        element._inDirtyList = true;
        assert(() {
          if (debugPrintScheduleBuildForStacks)
            debugPrint('...dirty list is now: $_dirtyElements');
          return true;
        }());
      }
    
    

3 小结

'State.setState()'函数将当前State携带的Element对象加入到BuildOwner对象的dirtyList集合中,等待下帧绘制时重绘。

你可能感兴趣的:(Flutter笔记——State.setState发生了什么(源码学习))