Flutter 生命周期及渲染原理

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

Widget 生命周期

生命周期的基本概念

什么是生命周期

我们使用一个对象的时候,有时会需要知道对象的一个状态,什么时候被创建,什么时候被销毁。我们常用的生命周期方法其实本质上就是回调函数,是 Flutter 封装好的,在 Widget 的不同状态设置对应的回调方法给外部使用。

生命周期的作用

  1. 初始化数据
  • 创建变量、常量
  • 发送网络请求
  1. 监听小部件的事件
  2. 管理内存
  • 销毁数据、销毁监听者、定时器等

Widget 常见的生命周期函数

Widget 生命周期函数我们可以分为两种类型来看,无状态与有状态类型。

无状态 Widget (StatelessWidget)

当无状态 Widget 比渲染的时候会依次调用构造函数与 build 函数。

有状态 Widget (StatefulWidget)

class MyHomePage extends StatefulWidget {
  final String? title;
  MyHomePage({this.title}) {
    print('Widget 构造函数被调用了');
  }
  @override
  _MyHomePageState createState() {
    print('createState 函数被调用了');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState() {
    print('State 构造函数被调用了');
  }
  @override
  void initState() {
    super.initState();
    print('State 的 init 函数被调用了');
  }
  @override
  Widget build(BuildContext context) {
    print('State build 函数被调用了');
    return Center(child: Text(widget.title ?? ''),);
  }

  @override
  void dispose() {
    super.dispose();
    print('State 的 dispose 函数被调用了');
  }
}
复制代码

有状态的 Widget 被渲染的时候会依次调用 StatefulWidget 的构造函数、createState 函数、State 的构造函、initState 函数、build 函数。当热重载的时候会调用 StatefulWidget 的构造函数跟 Statebuild 函数。当调用 setState 方法的时候会调用 build 函数。

通过源码可以看到 setState 其实就是 _element 调用了 markNeedsBuild 函数,只是在此之前做了一些错误的判断,这里 _element 就是 context

数据共享部件 InheritedWidget

class MyData extends InheritedWidget {
  final int data; //需要在子组件中共享的数据

  //构造方法
  const MyData({required this.data, required Widget child}) : super(child: child);

  //定义一个便捷方法,方便子组件中的 widget 获取共享数据
  static MyData? ofContext(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

  //该回调决定当前 data 发生变化的时候,是否通知子组件(依赖 data 的子组件)
  @override
  bool updateShouldNotify(covariant InheritedWidget oldWidget) {
    print('调用了 updateShouldNotify 函数');
    //如果返回 true,子部件中依赖共享数据的 Widget(build 函数中是否使用共享数据) 的 didChangeDependencies 方法会被调用
    return (oldWidget as MyData).data != data;
  }
}

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

  @override
  _InheritedDemoState createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return MyData(data: _count, child: Column(
      children: [
        TextDemo(count: _count),
        ElevatedButton(onPressed: () {
          _count++;
          setState(() {});
        }, child: const Icon(Icons.add)),
      ],
    ));
  }
}

class TextDemo extends StatefulWidget {
  final int? count;
  TextDemo({this.count});
  @override
  _TextDemoState createState() => _TextDemoState();
}

class _TextDemoState extends State<TextDemo> {
  @override
  Widget build(BuildContext context) {
    print('调用了 build 函数');
    return Text((MyData.ofContext(context) as MyData).data.toString());
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print('调用了 didChangeDependencies 函数');
  }
}
复制代码

在我们开发的过程中一定会遇到这种场景,子部件的数据需要依赖父部件的数据,当层级比较多的话层层传递的方式就会比较麻烦,所以 Flutter 提供一个 InheritedWidget 部件,用来解决这种场景。如上案例中,我们定义了一个负责数据共享的类 MyData 继承于 InheritedWidgetMyData 的构造方法中有两个参数,data 代表需要共享的数据,child 表示依赖于数据共享的 Widget。并且我们提供了一个 ofContext 方法,供外界获取数据时候使用。使用的话,我们在 _InheritedDemoStatebuild 方法中初始化 MyData,在需要获取共享数据的子部件中通过 MyData.ofContext(context) 来获取数据,这里需要注意的是,子组件需要通过 InheritedWidget 来获取共享数据的话,其根组件必须是继承于 InheritedWidget 类的 MyData。当我们执行 _count++ 的时候会调用 updateShouldNotify 方法,在这里我们可以通过返回值来判断是否调用子组件的 didChangeDependencies 方法,类似于发送通知,返回值为 true 的时候就会调用,反之就不调用,我们可以根据需求在 didChangeDependencies 方法中做一些事情。

Flutter 渲染原理

abstract class Widget extends DiagnosticableTree {
  Element createElement();
}
复制代码

以上只附上了关键代码,通过源码我们可以看到 Widget 类都会实现 createElement 函数。这里我们对于 Widget 的子类 StatelessWidgetStatefulWidget 的渲染流程分开来看。

StatelessWidget 渲染流程

abstract class StatelessWidget extends Widget {
  StatelessElement createElement() => StatelessElement(this);
}
复制代码

StatelessWidgetcreateElement 方法会创建一个 StatelessElement 对象并加入到 Elment 树中,并且返回 StatelessElement 对象,创建 StatelessElement 对象的时候 StatelessWidget 自己作为参数传给 StatelessElement 对象。

class StatelessElement extends ComponentElement {
  Widget build() => widget.build(this);
}
复制代码
abstract class ComponentElement extends Element {
  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(_child == null);
    assert(_lifecycleState == _ElementLifecycle.active);
    _firstBuild();
    assert(_child != null);
  }
复制代码
abstract class Element extends DiagnosticableTree implements BuildContext {
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;
复制代码

通过源码追踪可以看到 StatelessElement 继承于 ComponentElementComponentElement 继承于 Element,在 Element 的构造方法中外部传入的 widget 会被赋值给 _widget 属性,在 ComponentElement 中会调用 mount 方法, mount 方法中的 _firstBuild 最终会执行 ComponentElementbuild 方法,并且 StatelessElementbuild 会执行 widget.build(this),并把自己传给外面。

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {}
}
复制代码

所以我们外部 StatelessWidget 子类中的 context 就是 Element 对象。

StatefulWidget 渲染流程

abstract class StatefulWidget extends Widget {
  @override
  StatefulElement createElement() => StatefulElement(this);
}
复制代码

StatefulWidget 同样会执行 createElement,但是返回的对象是 StatefulElement 类型。

class StatefulElement extends ComponentElement {
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
        super(widget) {
        state._element = this;
        state._widget = widget;
}

  @override
  Widget build() => state.build(this);
}
复制代码

但是 StatefulElement 多了一步就是执行 createState 函数,并且赋值给 _state 属性,并且把外部传入的 widget 赋值给 state._widget 属性,把 this 指针赋值给 state._element 。这也是我们能在 state 中能获取到 widget 的原因。这里 build 方法中执行的是 state.build(this)

Guess you like

Origin juejin.im/post/7035278749050339342