flutter中关于State的生命周期

一.关于State的个人介绍


每一个StatefulWidget都会对应一个Statestate主要用来存储StatefulWidget的可变数据,当数据变化的时候触发UI更新。state对象在StatefulWidget重建时(例如父组件setState 触发重建),只要其中树的位置(key相同)未变,关联的state对象就不会被销毁,这就意味着State的生命周期要比StatefulWidget实例更长。

二.举个例子


下面是一段员工和老板关于薪资的代码,我们看一下经过一系列的操作,state的周期函数都是怎么执行的,以及执行的顺序是什么样的。

/// 老板
class Boss extends StatelessWidget {
  const Boss({super.key});

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        //导航到新路由
        Navigator.push(
          context,
          MaterialPageRoute(builder: (context) {
            return const Worker(salay: 7000,);
          }),
        );
      },
      child: const Scaffold(
        body: Center(
          child: Text("我是老板"),
        ),
      ),
    );
  }
}

点击老板可以进入员工薪资的页面

/// 打工的
class Worker extends StatefulWidget {
  final int salay;
  const Worker({super.key, required this.salay});

  
  State<Worker> createState() => _ChildWidgetState();
}

class _ChildWidgetState extends State<Worker> {
  /// 你得到的工资
  int _salary = 0;

  
  void initState() {
    super.initState();
    /// 初始化状态
    _salary = widget.salay;
    print('执行initState函数');
  }


  
  Widget build(BuildContext context) {
    print('执行build函数');
    return Scaffold(
      body: Center(
        child: TextButton(
          child: Text('你得到的工资是:${_salary}'),
          onPressed: () {
            setState(() {
              _salary = _salary + 2000;
            });
          },
        ),
      ),
    );
  }

  
  void didUpdateWidget(Worker oldWidget) {
    super.didUpdateWidget(oldWidget);
    print('执行didUpdateWidget函数');
  }

  
  void deactivate() {
    super.deactivate();
    print('执行deactivate函数');
  }

  
  void dispose() {
    super.dispose();
    print('执行dispose函数');
  }

  
  void reassemble() {
    super.reassemble();
    print('执行reassemble函数');
  }

  
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('执行didChangeDependencies函数');
  }
}

在员工页面,每次点击一下你的工资,你的工资都会增加2000块。

1.运行应用,并打开“我是老板”的页面。此时点击“我是老板”,进入员工薪资页面,此时页面显示的是:你得到的工资是7000,可以看到工作台的输出结果是:
在这里插入图片描述

2.紧接着点击员工薪资页面上的“你得到的工资是7000”,页面上的内容变成了:你得到的工资是9000,控制台的输出结果是:

3.点击⚡️按钮热重载,控制台输出:
在这里插入图片描述
4.侧滑返回上一个页面,控制台输出:
在这里插入图片描述

三.关于State的各个生命周期函数


createState():当statefulWidget被创建的时候,就会通过createState()创建与之关联的state对象,这个步骤是由框架自动调用的。


initState():在 State 对象被创建并且绑定到 Widget 上时调用,对于每一个State对象,Flutter 框架只会调用一次该回调,所以,通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等。

didChangeDependencies():当State对象的依赖发生变化时会被调用。典型的场景是当系统语言 Locale 或应用主题改变时,Flutter 框架会通知 widget 调用此回调。

需要注意,组件第一次被创建后挂载的时候(包括重创建)对应的didChangeDependencies()也会被调用。


build():主要用来构建widget子树,在如下常见被调用:

  1. 在调用initState()之后。
  2. 在调用didUpdateWidget()之后。
  3. 在调用setState()之后。
  4. 在调用didChangeDependencies()之后。
  5. 在State对象从树中一个位置移除后(会调用deactivate())又重新插入到树的其他位置之后。

reassemble():仅存在于开发调试场景,在热重载(hot reload)时会被调用,Release模式下永远不会被调用。

didUpdateWidget ():在 widget 重新构建时,Flutter 框架会调widget.canUpdate来检测 widget 树中同一位置的新旧节点,然后决定是否需要更新,如果widget.canUpdate返回true则会调用此回调。

widget.canUpdate会在新旧 widget 的 keyruntimeType 同时相等时会返回true,也就是说在在新旧 widget 的keyruntimeType同时相等时didUpdateWidget()就会被调用。

deactivate():当 State 对象从树中被移除时,会调用此回调。

在一些场景下,Flutter 框架会将 State 对象重新插到树中,如包含此 State 对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey 来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。


dispose():当 State 对象从树中被永久移除时调用;通常在此回调中释放资源。

四.你真的理解了吗?

尝试着思考回答一下这几个问题,看看你真的理解了吗?

  1. 当 StatefulWidget 第一次插入到 Widget 树时,哪些生命周期方法会被调用?顺序是什么?

首次插入时的正确顺序是:
initState → didChangeDependencies → build

  1. 当组件被永久移除时,哪些方法会被调用?它们的区别是什么?

当组件被永久移除时,deactivate,dispose分别会被调用,deactivate是在从组件树上移除的时候执行,dispose是在永久销毁的时候执行。

  1. 假设父组件更新了传递给子组件的 color 属性,此时子组件的 didUpdateWidget 和 didChangeDependencies 是否会触发?为什么?

假设父组件更新了传递给子组件的 color 属性,此时子组件的 didUpdateWidget会被触发,didUpdateWidget 触发是因为父组件传递的 Widget 配置(color)变化。didChangeDependencies 不触发,因为 color 是直接通过父组件传递的 Widget 属性,不是通过 InheritedWidget 依赖的全局状态。

  1. 如果子组件依赖了一个全局的 Theme(通过 Theme.of(context)),当主题颜色变化时,会触发哪个回调?

如果子组件依赖了一个全局的 Theme(通过 Theme.of(context)),当主题颜色变化时,会触发didChangeDependencies回调,因为 Theme.of(context) 依赖 InheritedWidget。

  1. 在 initState 中调用 setState 是否安全?为什么?

在 initState 中调用 setState 是安全的!Flutter 允许在 initState 中调用 setState,但需注意:

  • 避免访问 BuildContext 依赖的 InheritedWidget(如 Theme.of(context)),因为此时组件尚未完全挂载。
  • 典型场景:初始化数据后立即更新 UI。
  1. 如果父组件未发生任何变化,但子组件内部调用 setState,哪些生命周期方法会被触发?

如果父组件未发生任何变化,但子组件内部调用 setState,只会触发build 方法

  1. 在开发过程中执行热重载(Hot Reload)时,哪些生命周期方法会被调用?为什么这个回调在 Release 模式不存在?

在开发过程中执行热重载(Hot Reload)时,reassemble会被调用,因为Release模式下不调用

  1. 当使用 GlobalKey 将组件从树的一个位置移动到另一个位置时,deactivate 和 dispose 是否会触发?为什么?

当使用 GlobalKey 将组件从树的一个位置移动到另一个位置时,只会触发 deactivate,不会触发 dispose!

  • 使用 GlobalKey 移动组件时,Flutter 会保留 State 对象,重新插入到新位置后不会调用 initState,而是直接复用之前的 State。
  1. 如果组件被移除后又重新插入到树中,它的 initState 会再次调用吗?为什么?

如果组件被移除后又重新插入到树中,它的 initState 不会再次调用,因为State 对象被复用时不会重新初始化。

  1. 以下代码有什么问题?如何修复?
class MyWidget extends StatefulWidget {
  const MyWidget({super.key});
  
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late final StreamSubscription _subscription;

  
  void initState() {
    super.initState();
    // 监听一个全局事件
    _subscription = eventBus.on().listen((event) {});
  }

  
  Widget build(BuildContext context) {
    return Text('Hello');
  }
}

有问题,代码的问题在于未在 dispose 中取消订阅,导致内存泄漏!


  void dispose() {
    _subscription.cancel(); // 必须释放资源
    super.dispose();
  }

  1. 请描述以下操作的完整生命周期流程:
    • 组件首次加载。
    • 父组件更新属性。
    • 依赖的 InheritedWidget 变化。
    • 组件被移除并销毁。

首次加载:

  • initState → didUpdateWidget → build
  • 父组件更新属性:didChangeDependencies → build
  • 依赖的 InheritedWidget 变化:didUpdateWidget → build
  • 移除销毁:deactivate → dispose
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值