Flutter|源码分析State生命周期

247 阅读3分钟

如何知道State的生命周期?

网上搜索❎

打开源码✅

引言

重写的各个生命周期阶段的方法都是State类的,StatefulElement在初始化的时候会调用Widget.createState方法实例化State,并且持有State(_state)。所以各个周期都在StatefulElement类里面

initState

那么,在StatefulElement类里面搜索「state.initState(」就能看到init方法被调用的地方。

@override
void _firstBuild() {
  final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
  state.didChangeDependencies();
  super._firstBuild();
}

可以看到,是在Widget第一次build之前,调用了initState,然后接着调用didChangeDependencies

didChangeDependencies

接着搜索「state.didChangeDependencies(」,看到didChangeDependencies除了_firstBuild方法中被调用之外,还有一次在performRebuild调用之前被调用。而setState的流程中,节点被标脏,就会执行Element.rebuild→Element.performRebuild。所以可以理解,为什么大伙都说在更改依赖之后,会执行build方法。(详情查看provider实现和setState原理,大致链路为:InheritedElement.notifyClients→Element.didChangeDependencies→markNeedsBuild→performRebuild→build)

@override
void performRebuild() {
  if (_didChangeDependencies) {
    state.didChangeDependencies();
    _didChangeDependencies = false;
  }
  super.performRebuild();
}

didUpdateWidget

搜索「state.didUpdateWidget(」,只有一处调用,在父节点更新的时候,会调用updateChild,这时候会调用子组件的update方法(update方法只有这一种调用途径💃),这里面会调用didUpdateWidget,接着是rebuild→performRebuild→build。

注意⚠️:

这里可以看到,didUpdateWidget和didChangeDependencies是有重合的路径的。但是,这段重合,并不会发生在同一个节点。「在父节点的dependency改变之后,Inherited组件发出notify,让父组件调用自己的didChangeDependencies方法,接着父组件的state进入didChangeDependencies状态,然后更新自身并且更新child(通过child.update)方法,让子节点的state进入didUpdateWidget状态。」这段过程中,将「父节点」替换成「consumer」或者「selector」等组件,就完全理解了。详见Provider原理和setState原理

@override
void update(StatefulWidget newWidget) {
  super.update(newWidget);
  final StatefulWidget oldWidget = state._widget!;
  state._widget = widget as StatefulWidget;
  final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
  rebuild(force: true);
}

deactivate

在「framework.dart」文件里面搜索「state.deactivate(」,仅StatefulElement.deactivate方法出现一次。

当节点从树中被移除时调用。节点从树中移除有两种情况,一种是使用了GlobalKey复用组件,即改变使用了GlobalKey的组件的父节点时,先从当前位置移除,然后再插入另一位置;另一种是永久移除节点。

个人感觉,这是一个没那么重要的方法,不展开写了,如何仅调用deactivate,但不调用dispose见下面问题和链接👇

如何通过GlobalKey复用组件(不推荐)?

How to use GlobalKey to maintain widgets' states when changing parents?

dispose

在「framework.dart」文件里面搜索「state.dispose(」,只有在StatefulElement.unmount方法中出现一次。

当节点从树中永久移除时调用。一般在这里释放资源

@override
  void unmount() {
    super.unmount();
    state.dispose();
    state._element = null;
    // Release resources to reduce the severity of memory leaks caused by
    // defunct, but accidentally retained Elements.
    _state = null;
  }

demo:

上面涉及的didChangeDependenciesdidUpdateWidget两个方法的注意处的例子见下面demo:


///数据类
class Count extends ChangeNotifier {
  Count();
  int count = 0;

  void add() {
    count++;
    notifyListeners();
  }
}

///用于触发数据类变更
class DemoPage extends StatefulWidget {
  @override
  _DemoPageState createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  @override
  initState() {
    super.initState();
  }

  @override
  dispose() {
    super.dispose();
  }

  @override
  void didUpdateWidget(covariant DemoPage oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("demo page didUpdateWidget");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("demo page didChangeDependencies");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text("DemoPage"),
        ),
        body: Center(
          child: DemoWidget(),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            //注意这里listen为false,所以不会触发didChangeDependencies
            Count count = Provider.of<Count>(context, listen: false);
            count.add();
            print("count.count = ${count.count}");
          },
          child: const Icon(Icons.add),
        ));
  }
}

///用于验证didChangeDependencies
class DemoWidget extends StatefulWidget {
  @override
  _DemoWidgetState createState() => _DemoWidgetState();
}

class _DemoWidgetState extends State<DemoWidget> {
  Count? count;
  @override
  initState() {
    super.initState();
  }

  @override
  didChangeDependencies() {
    super.didChangeDependencies();
    //这里的listen为true,所以会触发didChangeDependencies
    count = Provider.of<Count>(context, listen: true);
    count?.addListener(() {
      setState(() {});
    });
    print("demo widget didChangeDependencies");
  }

  @override
  void didUpdateWidget(covariant DemoWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("demo widget didUpdateWidget");
  }

  @override
  dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return UpdateText(text:
      count == null ? "null" : count!.count.toString(),
    );
  }
}

///用于验证didUpdateWidget
class UpdateText extends StatefulWidget {
  const UpdateText({Key? key, required this.text}) : super(key: key);
  final String text;
  @override
  _UpdateTextState createState() => _UpdateTextState();
}

class _UpdateTextState extends State<UpdateText> {
  @override
  initState() {
    super.initState();

  }

  @override
  dispose() {
    super.dispose();

  }

  @override
  void didUpdateWidget(covariant UpdateText oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("UpdateText didUpdateWidget");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("UpdateText didChangeDependencies");
  }

  @override
  Widget build(BuildContext context) {
    return Text(widget.text);
  }
}

///入口类
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChangeNotifierProvider<Count>(
        create: (_) => Count(),
        child: DemoPage(),
      ),
    );
  }
}

/*
点击之后输出:
flutter: count.count = 1
flutter: demo widget didChangeDependencies
flutter: UpdateText didUpdateWidget
*/

附上周期图

1.png

最后有个注意点

在initState里面无法使用Provider.of<>(context, listen: true)

如果需要依赖某个ChangeNotifier类型并且每次改变都刷新数据的话,需要在didUpdateWidget方法中使用Provider.of<>(context, listen: true)

如果仅仅是获取ChangeNotifier类型对象,使用数据,不依赖改变的话,可以直接在initState里面Provider.of<>(context, listen: false)获取对象,这样当前的context不会被注册进刷新列表,也就不会出现下面的报错。

2.png