StatefulWidget

496 阅读2分钟

Flutter组件构建浅析中我们为了简便起见,以StatelessWidget为例梳理了其构建的过程,如果用StatefulWidget替换,其流程不会有大的变化,只是多了一些细节。

StatefulWidget类图

下面这张图方便查看对象之间的关系和其中的方法 state.png

StatefulWidget示例代码

使用StatefulWidget的示例代码,在屏幕上点一次更换一个颜色

class Start extends StatefulWidget {
  const Start({Key? key}) : super(key: key);

  @override
  State<Start> createState() => _StartState();
}

class _StartState extends State<Start> {
  
  @override
  void initState() {
    super.initState();
    debugPrint("Start didChangeDependencies");
  }

  Color _color() {
    return Color.fromARGB(
      255,
      random.nextInt(255),
      random.nextInt(255),
      random.nextInt(255),
    );
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    debugPrint("Start didChangeDependencies");
  }

  @override
  void didUpdateWidget(covariant Start oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 本示例不会打印
    debugPrint("Start didUpdateWidget");
  }

  @override
  Widget build(BuildContext context) {
    debugPrint("Start build");
    // 在屏幕上点一次更换一个颜色
    return GestureDetector(
      child: ColoredBox(color: _color()),
      onTap: () {
        setState(() {
        });
      },
    );
  }
}

流程相似之处不再重述了,细节可查看Flutter组件构建浅析,我们通过下面这张图从初始化setState更新两个方面来说一下不同之处

flutter组件build流程 (16).png

初始化

  1. createElement的时候实例化了State,并使其持用了Element和Widget引用
StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
    state._element = this;
    state._widget = widget;
  }

  1. _firstBuild时调用了initState和didChangeDependencies方法。initState只在此处调用一次,是经常要复写的方法,而didChangeDependencies方法很少复写
  void _firstBuild() {
    try {
      final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
    } 
    state.didChangeDependencies();
    super._firstBuild();
  }
  1. performRebuild先判断了_didChangeDependencies是否为true,初始化是false,InheritedElement会涉及到这个值,这个先按下不表。
  @override
  void performRebuild() {
    if (_didChangeDependencies) {
      state.didChangeDependencies();
      _didChangeDependencies = false;
    }
    super.performRebuild();
  }
  1. build方法调用的是State中的build方法,也是我们经常复写的方法
@override
  Widget build() => state.build(this);

setState更新

就上面的示例代码来说,当点击屏幕调用到setstate方法,会触发当前StatefulElement重新build,和初始化的区别在于此时childElement不为空了,更新操作走的是childElement的update方法

if (child != null) {
      bool hasSameSuperclass = true;
    if (hasSameSuperclass && child.widget == newWidget) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        newChild = child;
        // 新建的GestureDetector和之前的runtimeType、key一样,可以更新
      } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot) {
          updateSlotForChild(child, newSlot);
        }
        // 走的是update
        child.update(newWidget);
        newChild = child;
      } else {
        deactivateChild(child);
        newChild = inflateWidget(newWidget, newSlot);
      }
}

不同的子类update方法的实现不一样,StatelessElement比较简单直接重建走performRebuild,示例代码中的GestureDetector就是这种。如果是StatefulElement,会先调用state.didUpdateWidget方法,这个方法是经常需要复写的,主要有两个操作

  • 移除oldWidget上面的一些监听之类的
  • 使用newWidget的配置更新State
StatefulElement

  @override
  void update(StatefulWidget newWidget) {
    super.update(newWidget);
    _dirty = true;
    // 指向了新widget
    state._widget = widget as StatefulWidget;
    try {
      ...
      final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
    } 
    rebuild();
  }

为什么流程图上这个state.didUpdateWidget的颜色和上面state方法的颜色标记不一样,因为它们是属于不同StatefulElement。还有一些点打算放在InheritedWidget中分析