Flutter Widget生命周期

  • 基本代码

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

  @override
  State createState() {
    debugPrint('parent widget----------createState');
    return _ParentState();
  }
}

class _ParentState extends State {
  bool inContainer = false;

  @override
  void initState() {
    debugPrint('parent state----------initState');
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      debugPrint('parent state----------addPostFrameCallback');
    });
  }

  @override
  void didChangeDependencies() {
    debugPrint('parent state----------didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('parent state----------build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('one child'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              setState(() {
                inContainer = !inContainer;
              });
            },
            child: const Text('change'),
          ),
          const Child()
        ],
      ),
    );
  }

  @override
  void didUpdateWidget(covariant Parent oldWidget) {
    debugPrint('parent state----------didUpdateWidget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void activate() {
    debugPrint('parent state----------activate');
    super.activate();
  }

  @override
  void deactivate() {
    debugPrint('parent state----------deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    debugPrint('parent state----------dispose');
    super.dispose();
  }
}
class Child extends StatefulWidget {
  final v = 1;
  const Child({Key? key}) : super(key: key);

  @override
  State createState() {
    debugPrint('child widget----------createState');
    return _ChildState();
  }
}

class _ChildState extends State {
  @override
  void initState() {
    debugPrint('child state----------initState');
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
      debugPrint('child state----------addPostFrameCallback');
    });
  }

  @override
  void didChangeDependencies() {
    debugPrint('child state----------didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    debugPrint('child state----------build');
    return const Text('cus-widget');
  }

  @override
  void didUpdateWidget(covariant Child oldWidget) {
    debugPrint('child state----------didUpdateWidget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void activate() {
    debugPrint('child state----------activate');
    super.activate();
  }

  @override
  void deactivate() {
    debugPrint('child state----------deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    debugPrint('child state----------dispose');
    super.dispose();
  }
}

初始渲染后的打印:

flutter: parent widget----------createState
flutter: parent state----------initState
flutter: parent state----------didChangeDependencies
flutter: parent state----------build
flutter: child widget----------createState
flutter: child state----------initState
flutter: child state----------didChangeDependencies
flutter: child state----------build
flutter: parent state----------addPostFrameCallback
flutter: child state----------addPostFrameCallback

点击change按钮打印:

flutter: parent state----------build

修改Childv变量的值之后热更新:

flutter: parent state----------build
flutter: child state----------didUpdateWidget
flutter: child state----------build

修改ChildColumn中的顺序,或者不改变层级使用LocalKey也会调用didUpdateWidget。因为Widget本身不可变,当你修改了Widget的属性之后或者直接rebuild不可避免需要重新创建一个新的Widget替换旧的,而同时旧的State可以复用,就会调用StatedidUpdateWidget

  • 关于deactivate

Parentbuild方法修改如下:

Widget build(BuildContext context) {
    debugPrint('parent state----------build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('one child'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              setState(() {
                inContainer = !inContainer;
              });
            },
            child: const Text('change'),
          ),
          inContainer
              ? Container(
                  color: Colors.blue,
                  child: const Child(key: ValueKey('123')),
                )
              : const Child(key: ValueKey('123'))
        ],
      ),
    );
  }

点击change按钮

flutter: parent state----------build
flutter: child widget----------createState
flutter: child state----------initState
flutter: child state----------didChangeDependencies
flutter: child state----------build
flutter: child state----------deactivate
flutter: child state----------dispose
flutter: child state----------addPostFrameCallback

再次点击change按钮

flutter: parent state----------build
flutter: child state----------deactivate
flutter: child widget----------createState
flutter: child state----------initState
flutter: child state----------didChangeDependencies
flutter: child state----------build
flutter: child state----------dispose
flutter: child state----------addPostFrameCallback

需要注意,两次打印deactivate位置变了,由于两次点击ChildState的层级不同,优先处理更深层次,再处理父层。

尽管有Key,但是由于层级改变了,所有不会复用State

  • 关于activate

Childbuild修改如下:

  final GlobalKey _gk = GlobalKey();

  @override
  Widget build(BuildContext context) {
    debugPrint('parent state----------build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('one child'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              setState(() {
                inContainer = !inContainer;
              });
            },
            child: const Text('change'),
          ),
          inContainer
              ? Container(
                  color: Colors.blue,
                  child: Child(key: _gk),
                )
              : Child(
                  key: _gk,
                )
        ],
      ),
    );
  }

点击change按钮,打印如下:

flutter: parent state----------build
flutter: child state----------deactivate
flutter: child state----------activate
flutter: child state----------didUpdateWidget
flutter: child state----------build

这里可以看到activate的打印,因为使用了GlobalKey,且改变了Child的层级,其Staterebuild过程中从树中移除后又重新插入新的位置。

  • 关于didChangeDependencies

可以看到初始化渲染调用了child-didChangeDependencies,这是因为最开始child并不存在,在parent动用updateChild的时候不存在旧的child,那么会直接调用inflateWidget->mount->_firstBuild->didChangeDependencies

inflateWidget中,会判断‘GlobalKey’,即能不能在inactiveElement之中找到可用的Element,如果找到了就不用重新创建新的Element,也就不会调用didChangeDependencies

如果没找到可用的Element,就会创建新的Element,这个时候就会调用mount方法,将新的Element插入树中,注意由于Element.mount不调用_firstBuild,只有ComponentElement他重写了mount,其中调用了_firstBuild。之所以会调用ComponentElement.mount是因为StatefulElement继承自ComponentElement,而我们demo中的ChildStatefulWidget,对应的Element就是StatefulElement

在大部分情况下,调用didChangeDependencies的时候都会调用initState,那么在什么时候才只会调用didChangeDependencies?在使用InheritedWidget的时候,代码如下:

class ShareDataWidget extends InheritedWidget {
  final int data;

  const ShareDataWidget(
    this.data, {
    Key? key,
    required Widget child,
  }) : super(key: key, child: child);

  static ShareDataWidget? of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

  @override
  bool updateShouldNotify(covariant ShareDataWidget oldWidget) {
    return oldWidget.data != data;
  }
}

Parentbuild修改如下:

@override
  Widget build(BuildContext context) {
    debugPrint('parent state----------build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('one child'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () {
              setState(() {
                inContainer = !inContainer;
              });
            },
            child: const Text('change'),
          ),
          ShareDataWidget(
            inContainer ? 1 : 2,
            child: const Child(),
          )
        ],
      ),
    );
  }

这个时候点击change按钮打印如下:

flutter: parent state----------build
flutter: child state----------didChangeDependencies
flutter: child state----------build

当ShareDataWidget只是一个普通Widget的时候,打印:

flutter: parent state----------build

这里已经很清晰了,Parent每次build其实是重新运行了build函数内的代码,那么其中的Widget必定会改变,而对应的ElementState则尽量不改变。那么如果没有特殊的变化,单纯rebuild是不会触发除build之外的任何生命周期函数。而如果使用了InheritedWidget,那么当其携带的信息改变时就有必要通知依赖它的State,这里就是通过didChangeDependencies告知这种变化。

你可能感兴趣的:(Flutter Widget生命周期)