美文网首页
Flutter 的生命周期

Flutter 的生命周期

作者: QiShare | 来源:发表于2021-10-26 14:14 被阅读0次

    概述

    生命周期是一个widget组件加载到卸载的整个周期,熟悉生命周期可以让我们在合适的时机做该做的事情。

    在Flutter开发中,everything is widget,但我们一般都不用直接继承Widget类来实现一个新组件,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承Widget类来实现。StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是Flutter中非常重要的两个抽象类,它们引入了两种Widget模型。此文主要介绍这两种widget的生命周期。

    StatelessWidget

    StatelessWidget是无状态的Widget,一旦创建就不会发生变化,所以无法提供setState修改组件的状态,它内部属性应声明为final,防止意外发生改变。所以StatelessWidget的生命周期只有一个,就是 build,build 是用来创建 Widget 的,但因为 build 在每次界面刷新的时候都会调用,所以不要在 build 里写业务逻辑,可以把业务逻辑写到你的 StatelessWidget 的构造函数里。其生命周期如下图:

    image

    StatefulWidget

    StatefulWidget是有状态的Widget,它的state在发生变化时会重新渲染UI,提供setState方法修改组件的状态,它的生命周期主要在State这块,其生命周期如下图:

    image

    下面解释下各个函数:

    • initState:当Widget第一次插入到Widget树时会被调用;对于每一个State对象,Flutter framework只会调用一次该回调,所以,**通常在该回调中做一些一次性的操作,如状态初始化、订阅子树的事件通知等 **。不能在该回调中调用BuildContext.dependOnInheritedWidgetOfExactType(该方法用于在Widget树上获取离当前widget最近的一个父级InheritFromWidget),原因是在初始化完成后,Widget树中的InheritFromWidget也可能会发生变化,所以正确的做法应该在在build方法或didChangeDependencies中调用它。
    • didChangeDependencies():当State对象的依赖发生变化时会被调用;比如其所依赖的 InheritedWidget 发生变化时, Framework 会调用此方法通知组件发生变化。典型的场景是当系统语言Locale或应用主题改变时,Flutter framework会通知widget调用此回调。

    注意:didChangeDependencies 方法调用后,组件的状态变为 dirty,立即调用 build 方法。

    • build:用于构建Widget的,会在如下场景被调用:

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

    注意:此方法中应该只包含构建组件的代码,不应该包含其他额外的功能,尤其是耗时任务。

    • reassemble():此回调是专门为了开发调试而提供的,在热重载(hot reload)时会被调用,此回调在Release模式下永远不会被调用。
    • didUpdateWidget():在父widget重新构建子widget时,子widget的didUpdateWidget可能会被调用
      之所以说可能,是因为在父widget重新构建时,Flutter framework会调用Widget.canUpdate来检测Widget树中同一位置的新旧节点的key和runtimeType是否同时相等,新旧widget的key和runtimeType同时相等时会返回true,此时didUpdateWidget()就会被调用,如果新旧节点的key和runtimeType没有同时相等,didUpdateWidget()不会被调用。

    注意:Framework 调用完此方法后,会将组件设置为 dirty 状态,然后调用 build 方法。

    • deactivate():当State对象从树中被移除时,会调用此回调。在一些场景下,Flutter framework会将State对象重新插到树中,如包含此State对象的子树在树的一个位置移动到另一个位置时(可以通过GlobalKey来实现)。如果移除后没有重新插入到树中则紧接着会调用dispose()方法。
    • dispose():当State对象从树中被永久移除时调用;通常在此回调中释放资源。

    实例展示

    展示随机数的一个demo,点击随机按钮,更新随机数,打印此demo的生命周期函数的调用流程,代码如下:

    import 'dart:math';
    
    import 'package:flutter/material.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        return MaterialApp(
          home: Scaffold(
            body: RandomPage(),
          ),
        );
      }
    }
    
    class RandomPage extends StatefulWidget {
    
      @override
      State<StatefulWidget> createState() {
        // TODO: implement createState
        return _RandomPageState();
      }
    }
    
    class _RandomPageState extends State<RandomPage> {
      final _randomBuild = Random();
    
      int _randomValue = 0;
    
      @override
      void initState() {
        // TODO: implement initState
        super.initState();
        print('initState');
      }
    
     
      @override
      void didChangeDependencies() {
        // TODO: implement didChangeDependencies
        super.didChangeDependencies();
        print('didChangeDependencies');
      }
    
      @override
      Widget build(BuildContext context) {
        print('build');
        // TODO: implement build
        return Container(
          child: Padding(
            padding: EdgeInsets.only(top: 100),
            child: Center(
              child: Column(
                children: [
                  Text('随机数: $_randomValue'),
                  SizedBox(height: 200),
                  TextButton(
                    child: Text('切换随机数'),
                    onPressed: () {
                      setState(() {
                        _randomValue = _randomBuild.nextInt(10000);
                      });
                    },
                  )
                ],
              ),
            ),
          ),
        );
      }
    
      @override
      void reassemble() {
        // TODO: implement reassemble
        super.reassemble();
        print('reassemble');
      }
    
      @override
      void didUpdateWidget(covariant RandomPage oldWidget) {
        // TODO: implement didUpdateWidget
        super.didUpdateWidget(oldWidget);
        print('didUpdateWidget');
      }
    
      @override
      void deactivate() {
        // TODO: implement deactivate
        super.deactivate();
        print('deactivate');
      }
    
      @override
      void dispose() {
        // TODO: implement dispose
        super.dispose();
        print('dispose');
      }
    }
    

    点击run首次启动,打印log如下:

    flutter: initState
    flutter: didChangeDependencies
    flutter: build
    

    点击切换随机数按钮,打印log如下:

    flutter: build
    

    点击切换随机数按钮,会调用setState(),setState会调用build重新构建widget。

    点击hot reload按钮,打印log如下:

    flutter: reassemble
    flutter: didUpdateWidget
    flutter: build
    

    App生命周期

    通过WidgetsBindingObserver的didChangeAppLifecycleState 可以获取App的生命周期状态。生命周期在AppLifecycleState类中。常用状态包含如下几个:

    • resumed:处于可见并能响应用户的输入
    • inactive:处于不活跃状态且无法处理用户的响应
    • paused:处于不可见且不能响应用户的输入,但是在后台继续活跃

    举例如下:

    class _RandomPageState extends State<RandomPage>
        with WidgetsBindingObserver {
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance?.addObserver(this);
      }
    
      @override
      void dispose() {
        WidgetsBinding.instance?.removeObserver(this);
        super.dispose();
      }
    
      void didChangeAppLifecycleState(AppLifecycleState state) async {
        if (state == AppLifecycleState.resumed) {
          getData();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        // TODO: implement build
        throw UnimplementedError();
      }
      
      void getData() {
        
      }
    }
    

    记得注册和移除监听。

    生命周期相关的一些注意点

    • 页面渲染完毕回调
      addPostFrameCallback 是 StatefulWidge 渲染结束的回调,只会被调用一次,之后 StatefulWidget刷新 UI 也不会被调用。在initState不能调用BuildContext.dependOnInheritedWidgetOfExactType,可以使用addPostFrameCallback规避此限制:
    @override 
    void initState() { 
        super.initState(); 
        WidgetsBinding.instance.addPostFrameCallback((_) => {}); 
    }
    
    • mounted

    mounted 是 State 对象中的一个属性,此属性表示当前组件是否在树中(在创建 State 之后,调用 initState 之前,Framework 会将 State 和 BuildContext 进行关联),当 Framework 调用 dispose 时,mounted 被设置为 false,表示当前组件已经不在树中。

    createState 函数执行完毕后表示当前组件已经在组件树中,属性 mounted 被 Framework 设置为 true,平时写代码时或者看其他开源代码时经常看到如下代码:

    if(mounted){ 
        setState(() { ... });
    }
    

    强烈建议:在调用 setState 时加上 mounted 判断。

    为什么要加上如此判断?因为如果当前组件未插入到树中或者已经从树中移除时,调用 setState 会抛出异常,加上 mounted 判断,则表示当前组件在树中。

    • dirty 和 clean

    dirty 表示组件当前的状态为 脏状态,下一帧时将会执行 build 函数,调用 setState 方法或者 执行 didUpdateWidget 方法后,组件的状态为 dirty。

    clean 与 dirty 相对应,clean 表示组件当前的状态为干净状态,clean 状态下组件不会执行 build 函数。

    相关文章

      网友评论

          本文标题:Flutter 的生命周期

          本文链接:https://www.haomeiwen.com/subject/pqtyaltx.html