FlutterAndroid开发经验谈Android技术知识

Flutter Widget生命周期详解

2020-08-10  本文已影响0人  Joker_Wan

1 Widget 简介

在Flutter中,一切皆是Widget(组件),Widget的功能是“描述一个UI元素的配置数据”,它就是说,Widget其实并不是表示最终绘制在设备屏幕上的显示元素,而它只是描述显示元素的一个配置数据。

实际上,Flutter中真正代表屏幕上显示元素的类是 Element,也就是说Widget 只是描述 Element 的配置数据。并且一个 Widget 可以对应多个 Element,因为同一个 Widget 对象可以被添加到 UI树的不同部分,而真正渲染时,UI树的每一个 Element 节点都会对应一个 Widget 对象。

其中组件又包括无状态组件和有状态组件。

StatelessWidget 和 StatefulWidget 都是直接继承自 Widget 类,而这两个类也正是 Flutter 中非常重要的两个抽象类,它们引入了两种 Widget 模型。

2 两种Widget模型

2.1 StatelessWidget

@override
StatelessElement createElement() => new StatelessElement(this);

StatelessWidget相对比较简单,它继承自Widget类,重写了createElement()方法。

StatelessElement 间接继承自Element类,与StatelessWidget相对应(作为其配置数据)。

StatelessWidget用于不需要维护状态的场景,并且只会被渲染一次,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget。

2.2 StatefulWidget

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);

  @override
  StatefulElement createElement() => new StatefulElement(this);

  @protected
  State createState();
}

和StatelessWidget一样,StatefulWidget也是继承自Widget类,并重写了createElement()方法,不同的是返回的Element 对象并不相同;另外StatefulWidget类中添加了一个新的接口createState()。

StatefulElement 间接继承自Element类,与StatefulWidget相对应(作为其配置数据)。StatefulElement中可能会多次调用createState()来创建状态(State)对象。

createState() 用于创建和StatefulWidget相关的状态,它在StatefulWidget的生命周期中可能会被多次调用。例如,当一个StatefulWidget同时插入到widget树的多个位置时,Flutter framework就会调用该方法为每一个位置生成一个独立的State实例,其实,本质上就是一个StatefulElement对应一个State实例。

2.2.1 State

一个StatefulWidget类会对应一个State类,State表示与其对应的StatefulWidget要维护的状态,State中的保存的状态信息可以:

State中有两个常用属性:

  1. widget,它表示与该State实例关联的widget实例,由Flutter framework动态设置。注意,这种关联并非永久的,因为在应用生命周期中,UI树上的某一个节点的widget实例在重新构建时可能会变化,但State实例只会在第一次插入到树中时被创建,当在重新构建时,如果widget被修改了,Flutter framework会动态设置State.widget为新的widget实例。
  2. context,StatefulWidget对应的BuildContext,作用同StatelessWidget的BuildContext,表示当前widget在widget树中的上下文,每一个widget都会对应一个context对象(因为每一个widget都是widget树上的一个节点)。

3 生命周期

3.1 组件生命周期

Flutter 中说的生命周期,是独指有状态组件的生命周期,对于无状态组件生命周期只有一次 build 这个过程,也只会渲染一次。有状态组件的生命周期如下图:

Flutter-Widget生命周期.png

Flutter 中的生命周期,包含以下几个阶段:

Flutter 生命周期的整个过程可以分为四个阶段

  1. 初始化阶段:createState 和 initState
  2. 组件创建阶段:didChangeDependencies 和 build
  3. 触发组件 build:didChangeDependencies、setState 或者didUpdateWidget 都会引发的组件重新 build
  4. 组件销毁阶段:deactivate 和 dispose

3.2 组件首次加载过程

我们通过代码来看下组件首次加载执行的生命周期过程

在 lib 中创建 test_stateful_widget.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class TestStatefulWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print('create state');
    return TestState();
  }
}

class TestState extends State<TestStatefulWidget> {
  /// 定义 state [count] 计算器
  int count = 1;

  /// 定义 state [name] 为当前描述字符串
  String name = 'test';

  @override
  void initState() {
    print('init state');
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print('did change dependencies');
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(TestStatefulWidget oldWidget) {
    count++;
    print('did update widget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    print('deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    print('dispose');
    super.dispose();
  }

  @override
  void reassemble() {
    print('reassemble');
    super.reassemble();
  }

  void changeName() {
    setState(() {
      print('set state');
      this.name = 'Flutter';
    });
  }

  @override
  Widget build(BuildContext context) {
    print('build');
    return Column(
      children: <Widget>[
        FlatButton(
            child: Text('$name $count'), onPressed: () => this.changeName())
      ],
    );
  }
}

在 main.dart 中加载该组件

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      theme: ThemeData(
          primaryColor: Colors.amberAccent
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('生命周期测试'),
        ),
        body: Center(
          child: TestStatefulWidget(),
        ),
      ),
    );
  }
}

我们打开手机模拟器,然后运行该 App ,在输出控制台可以看到下面的运行打印日志信息:

I/flutter ( 7729): create state
I/flutter ( 7729): init state
I/flutter ( 7729): did change dependencies
I/flutter ( 7729): build

当我们点击 Android Studio 中的 Hot Reload 按钮(黄色小闪电 ⚡️)时控制台输出信息如下:


I/flutter ( 7729): reassemble
I/flutter ( 7729): did update widget
I/flutter ( 7729): build

3.3 触发组件再次 build

触发组件再次 build 有三种方式

  1. setState
  2. didChangeDependencies
  3. didUpdateWidget

setState的场景开发中你那个经常遇到,在数据状态变化时触发组件 build,在上面的代码运行后的界面中点击 test 文本按钮,然后观察控制台输出如下:

I/flutter ( 7729): set state
I/flutter ( 7729): build

didChangeDependencies场景:一般情况下我们会将一些比较基础的数据放到全局变量中,例如主题颜色、地区语言或者其他通用变量等。如果这些全局 state 发生状态变化则会触发该函数,而该函数之后就会触发 build 操作。

接下来看下didUpdateWidget 触发 build 的场景:
创建一个组件 SubStatefulWidget 继承 TestStatefulWidget

class SubStatefulWidget extends TestStatefulWidget {
  @override
  State<StatefulWidget> createState() {
    print('sub create state');
    return SubState();
  }
}

class SubState extends State<SubStatefulWidget> {

  @override
  void initState() {
    print('sub init state');
    super.initState();
  }

  @override
  void didChangeDependencies() {
    print('sub did change dependencies');
    super.didChangeDependencies();
  }

  @override
  void didUpdateWidget(TestStatefulWidget oldWidget) {
    print('sub did update widget');
    super.didUpdateWidget(oldWidget);
  }

  @override
  void deactivate() {
    print('sub deactivate');
    super.deactivate();
  }

  @override
  void dispose() {
    print('sub dispose');
    super.dispose();
  }

  @override
  void reassemble() {
    print('sub reassemble');
    super.reassemble();
  }

  void changeName() {
    setState(() {
      print('sub set state');
    });
  }

  @override
  Widget build(BuildContext context) {
    print('sub build');
    return Text('SubStatefulWidget');
  }
}

接着在 TestStatefulWidget 加载该组件

  @override
  Widget build(BuildContext context) {
    print('build');
    return Column(
      children: <Widget>[
        FlatButton(
            child: Text('$name $count'), onPressed: () => this.changeName()),
        SubStatefulWidget()
      ],
    );
  }

重新加载 APP,观看控制台输出如下:

I/flutter ( 8464): create state
I/flutter ( 8464): init state
I/flutter ( 8464): did change dependencies
I/flutter ( 8464): build
I/flutter ( 8464): sub create state
I/flutter ( 8464): sub init state
I/flutter ( 8464): sub did change dependencies
I/flutter ( 8464): sub build

上面日志先后打印了 TestStatefulWidget 与 SubStatefulWidget 四个状态函数 createState、initState、didChangeDependencies 和 build。当我们再次点击 test 文本按钮,观察控制台输出如下:

I/flutter ( 9425): set state
I/flutter ( 9425): build
I/flutter ( 9425): sub did update widget
I/flutter ( 9425): sub build

通过上面打印的日志我们可知,父组件调用setState不仅会触发自己 build,还会引发子组件重新 build ,虽然子组件没有任何改动。

3.4 触发组件销毁

在3.3的代码的基础上,我们直接在 TestStatefulWidget 组件中注释子组件 SubStatefulWidget 的调用

  @override
  Widget build(BuildContext context) {
    print('build');
    return Column(
      children: <Widget>[
        FlatButton(
            child: Text('$name $count'), onPressed: () => this.changeName()),
//        SubStatefulWidget()
      ],
    );
  }

然后点击 Android Studio 上的 Hot Reload 按钮观察控制台输出如下信息:

I/flutter ( 9425): reassemble
I/flutter ( 9425): sub reassemble
I/flutter ( 9425): did update widget
I/flutter ( 9425): build
I/flutter ( 9425): sub deactivate
I/flutter ( 9425): sub dispose

可以看到 SubStatefulWidget 被销毁

4 总结

  1. Flutter 中 Widget 分为两种:StatelessWidget 和 StatefulWidget
  2. Flutter 生命周期的整个过程可以分为四个阶段:初始化、组件创建、触发组件 build、组件销毁
  3. StatelessWidget 用于不需要维护状态的场景,只会 build 一次
  4. StatefulWidget 会被多次触发 build 函数,触发函数是setState、didChangeDependencies、didUpdateWidget
  5. 父组件调用setState不仅会触发自己 build,还会引发子组件重新 build ,虽然子组件没有任何改动
上一篇下一篇

猜你喜欢

热点阅读