Flutter生命周期与渲染原理

2021-12-19  本文已影响0人  浅墨入画

Widget生命周期

生命周期的基本概念
*   什么是生命周期
    *   说白了就是回调方法(函数)
    *   让使用者知道封装好的Widget当前处于什么状态
*   有什么作用
    *   监听Widget的事件
    *   初始化数据
        *    创建数据
        *    发送网络请求
    *    内存管理
        *    销毁数据、销毁监听者
        *    销毁Timer等等

查看无状态小部件的生命周期

// 忽略当前文件未使用key的报警
// ignore_for_file: use_key_in_widget_constructors, avoid_print

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Row;
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(),
        body: MyHomePage(title: 'Flutter Demo Page2'),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final String? title;
  MyHomePage({this.title}) {
    print('构造函数被调用了!');
  }

  Widget build(BuildContext context) {
    print('build方法被调用了!');
    // 上面title不传的话,默认展示123,这就是flutter中的空安全
    return Center(child: Text(title ?? '123'));
  }
}
查看日志

热重载的时候生命周期执行了一次。

问题:重新运行发现生命周期执行了两次
查看日志

实际上只执行了一次,多出来的一次是Android Studio的问题,我们不用担心。下面通过其他工具进行验证

Xcode运行查看日志

查看有状态小部件的生命周期

// 忽略当前文件未使用key的报警
// ignore_for_file: use_key_in_widget_constructors, avoid_print

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Row;
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(),
        body: MyHomePage(title: 'Flutter Demo Page2'),
      ),
    );
  }
}

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

  @override
  _MyHomePageState createState() {
    // TODO: implement createState
    print('createState来了!');
    return _MyHomePageState();
  }
}

// <MyHomePage>表示是MyHomePage对象的State
class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState() {
    print('State的构造方法');
  }
  
  @override
  void initState() {
    print('State的init方法');
    super.initState();
  }

  Widget build(BuildContext context) {
    print('State的build方法被调用了!');
    // 上面title不传的话,默认展示123,这就是flutter中的空安全
    return Center(child: Text(widget.title ?? '123'));
  }

  @override
  void dispose() {
    print('State的dispose');
    super.dispose();
  }
}
重新运行查看日志

State也是有状态的,它需要更新数据,当State状态发生变化,又是怎样的呢?下面给State添加数据

// <MyHomePage>表示是MyHomePage对象的State
class _MyHomePageState extends State<MyHomePage> {
  int _count = 0;

  _MyHomePageState() {
    print('State的构造方法');
  }

  @override
  void initState() {
    print('State的init方法');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    print('State的build方法被调用了!');
    return Column(
      children: [
        ElevatedButton(
            onPressed: () {
              _count++;
              setState(() {});
            },
            child: const Icon(Icons.add)),
        Text('$_count')
      ],
    );
  }

  @override
  void dispose() {
    print('State的dispose');
    super.dispose();
  }
}
热重载查看日志 点击+号按钮查看日志

点击之后只有State的build方法被调用了,也就是每次调用setState都会重新调用build方法。进入setState查看源码,主要方法是markNeedsBuild(),把setState(() {});替换成调用markNeedsBuild(),点击+号按钮依然能够触发State的build方法被调用了!

替换setState方法查看日志

数据共享InheritedWidget

有状态小部件还有一个生命周期方法didChangeDependencies

class _MyHomePageState extends State<MyHomePage> {
  int _count = 0;

  _MyHomePageState() {
    print('State的构造方法');
  }
  @override
  void initState() {
    print('State的init方法');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    print('State的build方法被调用了!');
    return Column(
      children: [
        ElevatedButton(
            onPressed: () {
              _count++;
              setState(() {});
            },
            child: const Icon(Icons.add)),
        Text('$_count')
      ],
    );
  }

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

  @override
  void didChangeDependencies() {
    print('didChangeDependencies');
    super.didChangeDependencies();
  }
}
重新运行查看日志

didChangeDependencies方法的调用在build调用之前,主要用于改变依赖关系;说到依赖关系,我们就不得不提InheritedWidget数据共享小部件

<!-- main.dart文件 -->
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Row;
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(),
        // 使用InheritedDemo小部件
        body: const InheritedDemo(),
      ),
    );
  }
}

<!-- inherited_demo.dart文件 -->
import 'package:flutter/material.dart';

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 Column(
      children: [
        Test1(_count),
        ElevatedButton(
            onPressed: () {
              _count++;
              setState(() {});
            },
            child: const Text('我是按钮'))
      ],
    );
  }
}

class Test1 extends StatelessWidget {
  final int count;
  const Test1(this.count);
  @override
  Widget build(BuildContext context) {
    return Test2(count);
    throw UnimplementedError();
  }
}

class Test2 extends StatelessWidget {
  final int count;
  const Test2(this.count);
  @override
  Widget build(BuildContext context) {
    return Test3(count);
    throw UnimplementedError();
  }
}

class Test3 extends StatefulWidget {
  final int count;
  const Test3(this.count);
  @override
  _Test3State createState() => _Test3State();
}

class _Test3State extends State<Test3> {
  @override
  void didChangeDependencies() {
    print('didChangeDependencies来了');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    return Text(widget.count.toString());
  }
}
重新运行查看日志

重新运行工程didChangeDependencies方法执行了一次,点击按钮didChangeDependencies方法并没有执行。

// 使用共享类,子组件之间就能共享数据
class _InheritedDemoState extends State<InheritedDemo> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    // MyData共享类
    return MyData(
        data: _count,
        child: Column(
          children: [
            Test1(_count),
            ElevatedButton(
                onPressed: () {
                  _count++;
                  setState(() {});
                },
                child: const Text('我是按钮'))
          ],
        )
    );
  }
}

// 使用了MyData的子组件才会依赖data的Widget
class _Test3State extends State<Test3> {
  @override
  void didChangeDependencies() {
    print('didChangeDependencies来了');
    super.didChangeDependencies();
  }

  @override
  Widget build(BuildContext context) {
    print('哥么我来了!');
    // 使用了MyData的子组件才会依赖data的Widget
    return Text(MyData.of(context)!.data.toString());
  }
}

// 数据共享类
class MyData extends InheritedWidget {
  final int data; //需要在子组件中共享的数据(保存点击次数)

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

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

  //该回调决定当前data发生变化时,是否通知子组件依赖data的Widget
  @override
  bool updateShouldNotify(MyData oldWidget) {
    //如果返回true,子部件中依赖数据的Widget(build函数中有数据)的didChangeDependencies会调用!
    return oldWidget.data != data;
  }
}
点击按钮成功触发didChangeDependencies方法
小结:Widget的生命周期
*   StatelessWidget
    *   1. 构造方法
    *   2. build方法
*   StatefulWidget(包含两个对象Widget 、State)
    *   Widget构造方法
    *   Widget的CreateState
    *   State的构造方法
    *   State的initState方法
    *   didChangeDependencies方法(改变依赖关系)
        *  依赖(共享数据)的InheritedWidget发生变化之后,didChangeDependencies方法才会调用!
    *   State的build
        *    当调用setState方法,会重新调用build进行渲染!
            *    setState方法内部主要是利用_element(本质就是context对象)调用markNeedsBuild()
    *   当Widget销毁的时候,调用State的dispose方法

Widget树与Render树

Widget的渲染原理

并不是所有的Widget都会被独立渲染,只有继承了RenderObjectWidget才会创建RenderObject对象;在Flutter渲染的流程中,有三棵重要的树,Flutter引擎是针对Render树进行渲染。

Widget树Element树Render树

查看Widget树

Element树

查看Element定义,发现它也是一个抽象类:

abstract class Element extends DiagnosticableTree implements BuildContext {
  
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  Element _parent;

  @override
  Widget get widget => _widget;
  Widget _widget;

  RenderObject get renderObject { ... }

  @mustCallSuper
  void mount(Element parent, dynamic newSlot) { ... }

  @mustCallSuper
  void activate() { ... }

  @mustCallSuper
  void deactivate() { ... }

  @mustCallSuper
  void unmount() { ... }

StatefulElementStatelessElement继承自ComponentElement, ComponentElement是继承自Element的抽象类。

abstract class ComponentElement extends Element { ... }
Element的生命周期:

StatelessWidget的Element

通过源码分析StatelessWidgetElement

查看源码 查看源码 查看源码 查看源码
Flutter断点调试源码的方式
添加断点调试 编辑断点

StatefulWidget的Element

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

  State<StatefulWidget> get state => _state;
  State<StatefulWidget> _state;
  ...
  @override
  Widget build() => state.build(this);
  ...
}

在创建StatefulElement实例时,会调用widget.createState()赋给私有变量_state,同时把widgetelement赋给_state,从而三者产生关联关系,它的build方法就是调用state.build(this),这里的this就是StatefulElement对象自己。

小结:Widget树Element树Render树
*  每一个Widget创建的时候都会创建一个Element对象
    *   调用createElement方法,Element加入Element树中,都会调用mount方法
    *   RenderElement主要是创建RenderObject对象
        *    通过mount方法创建RenderObject对象
    *    StatefulElement继承ComponentElement
        *    调用createState方法,创建state
        *    将Widget赋值给State对象
        *    调用state的build方法,并且将自己(Element)传出去
    *    StatelessElement继承ComponentElement
        *    StatelessWidget会创建Element
        *    然后Element创建就会调用mount方法
        *    mount里面会调用Widget的build方法进行渲染,并且将Element自己传出去
        *    主要调用build方法,并且将自己(Element)传出去
上一篇 下一篇

猜你喜欢

热点阅读