Flutter的生命周期&&渲染

115 阅读8分钟

Flutter的生命周期

生命周期的概念:

  • 什么是生命周期:

    • 生命周期就是回调函数
    • 让外界知道封装好的这个Widget处于什么状态
  • 生命周期的作用:

    • 初始化数据:

      • 创建常量、变量
      • 发送网络请求
    • 监听小部件的事件

    • 管理内存:

      • 销毁数据、销毁监听者
      • 销毁Timer

StatelessWidget生命周期探究

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('生命周期Demo')
        ),
        body: MyHomePage(title: 'Stateless生命周期Demo'),
      )
    );
  }
}

class MyHomePage extends StatelessWidget {
   MyHomePage({super.key,required this.title}) {
    print('调用了构造方法');
  }

  final String title;

  @override
  Widget build(BuildContext context) {
    print('调用build方法');
    return Center(
      child: Text(title),
    );
  }

}

image.png

这时发现调用了两次,但这里其实应该只会调用一次,接下来可以使用XcodeFlutter命令来验证

image.png

使用命令flutter run -d 'iPhone 13 Pro Max'

image.png

调用热启动或者热重载最后打印结果都只有一次

StatefulWidget生命周期探究

class MyHomePage extends StatefulWidget {

  String title;
  MyHomePage({Key? key, required this.title}) : super(key: key){
    print('调用widget的构造方法');
  }

  @override
  State<MyHomePage> createState() {
    print('调用createState');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {

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

  @override
  void initState() {
    // TODO: implement initState
    print('调用State init');
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    print('调用State build');
    return Container();
  }

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    print('调用didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    print('调用dispose');
    super.dispose();
  }
}

image.png

这里打印的顺序与我们预见的是相同的,那么接下来要稍加改造,加入一些数据更新

class MyHomePage extends StatefulWidget {

  String title;
  MyHomePage({Key? key, required this.title}) : super(key: key){
    print('调用widget的构造方法');
  }

  @override
  State<MyHomePage> createState() {
    print('调用createState');
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {

  int _count = 0;

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

  @override
  void initState() {
    // TODO: implement initState
    print('调用State init');
    super.initState();
  }

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

  @override
  void didChangeDependencies() {
    // TODO: implement didChangeDependencies
    print('调用didChangeDependencies');
    super.didChangeDependencies();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    print('调用dispose');
    super.dispose();
  }
}

image.png

这里通过点击按钮来对_count进行自增操作,并设置setState方法来重新调用build方法,那么接下来进入setState方法来看看

image.png

在这个方法中可以看到除了断言外,最重要的应当是_element!.markNeedsBuild();这一句调用,其中_element的定义则是StatefulElement? _element;

image.png

State中的get方法可以看出,context的本质就是_elemnet,接下来可以进行验证

 @override
  Widget build(BuildContext context) {
    print('调用State build');
    return Column(
      children: [
        ElevatedButton(
          onPressed: (){
            // setState(() {
            //   _count++;
            // });
            (context as StatefulElement).markNeedsBuild();
          },
          child: const Icon(Icons.add),
        ),
        Text('$_count'),
      ],
    );
  }

这里仅仅需要在State中的build方法中进行改变,将context强转类型后调用markNeedsBuild()方法,结果发现同样实现了setState的作用,但是这里并不建议直接这样调用,因为setState存在很多判断,这样更为安全。

共享数据 InheritedWidget

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

  @override
  State<InheritedDemo> createState() => _InheritedDemoState();
}

class _InheritedDemoState extends State<InheritedDemo> {

  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Test1(count: count),
        ElevatedButton(
            onPressed: (){
            count += 1;
            setState(() {

            });
        },
            child: const Text('按钮'))
      ],
    );
  }
}

class Test1 extends StatelessWidget {
  final int count;
  const Test1({Key? key, required this.count}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('Test1---$count');
    return Test2(count: count);
  }
}

class Test2 extends StatelessWidget {
  final int count;
  const Test2({Key? key,required this.count}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('test2---$count');
    return Test3(count: count);
  }
}

class Test3 extends StatefulWidget {
  final int count;
  const Test3({Key? key, required this.count}) : super(key: key);

  @override
  State<Test3> createState() => _Test3State();
}

class _Test3State extends State<Test3> {
  @override
  Widget build(BuildContext context) {

    return Center(
      child: Text(widget.count.toString()),
    );
  }
}

如果有需求如上,需要嵌套去传递数据,并且如上述代码中,每次更新数据需要通过Test1-->Test2-->Test3,这样可能会造成不必要的传递,因为可能只有Test3需要,而其它组件并不需要,这样便引出了共享数据InheritedWidget

那么代码需要更改如下(InheritedWidget代码块为inh)

class ShareData extends InheritedWidget {
  final int dataCount;

  const ShareData({
    Key? key,
    required this.dataCount,
    required Widget child,
  }) : super(key: key, child: child);
  //定义一个便捷方法,方便子树中的Widget获取共享数据
  static ShareData of(BuildContext context) {
    final ShareData? result =
        context.dependOnInheritedWidgetOfExactType<ShareData>();
    assert(result != null, 'No ShareData found in context');
    return result!;
  }

  // 当数据变化时
  @override
  bool updateShouldNotify(ShareData oldWidget) {
    return oldWidget.dataCount != dataCount;
  }
}

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

  @override
  State<InheritedDemo> createState() => _InheritedDemoState();
}

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

  @override
  Widget build(BuildContext context) {
    return ShareData(
        dataCount: count,
        child: Column(
          children: [
            const Test1(),
            ElevatedButton(
                onPressed: () {
                  count += 1;
                  setState(() {});
                },
                child: const Text('按钮'))
          ],
        ));
  }
}

class Test1 extends StatelessWidget {

  const Test1({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Test2();
  }
}

class Test2 extends StatelessWidget {
  const Test2({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const Test3();
  }
}

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

  @override
  State<Test3> createState() => _Test3State();
}

class _Test3State extends State<Test3> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(ShareData.of(context).dataCount.toString()),
    );
  }

  @override
  void didChangeDependencies() {
    print('Test3 -- didChangeDependencies');
    super.didChangeDependencies();
  }
}

渲染

Widget树和Render树

Flutter中有三棵树,分别为Widget树Element树Render树,它们之间的关系可以看做Widget是数据层,Render是渲染层,而Element是一个虚拟层,数据层和渲染层不直接进行交互而是通过虚拟层来完成

Flutter引擎渲染是针对于Render树中的对象进行渲染,而并不是所有的Widget对象都会被渲染,只有继承于RenderObjectWidget的对象才可以创建RenderObject并加入RenderObject树中,从而进行渲染

不同的Widget:

  • 不会被渲染的Container:

    • Container就是不会被渲染的一个Widget,那么可以先来看看其继承关系
    • Container-->StatelessWidget-->Widget,其中并不存在RenderObjectWidget,所以它并不会被渲染
  • 会被渲染的Row:

    • Row是一个会被渲染的Widget,先来看看其继承关系
    • Row-->Flex-->MultiChildRenderObjectWidget-->RenderObjectWidget
    • Flex实现了createRenderObject方法,返回值是RenderFlex对象,其实就是RenderObject

image.png

Element树

刚才讨论了并不是所有的Widget都会被渲染,但是所有的Widget都会创建Element

RowElement树

这里还是先从Row讲起,其继承关系是:Row-->Flex-->MultiChildRenderObjectWidget-->RenderObjectWidget

RenderObjectWidget是一个抽象类,其中有一个方法是createElement(),并且此方法的具体实现是位于MultiChildRenderObjectWidget

image.png

这里很简单只有一句代码,调用MultiChildRenderObjectElement的构造方法,并且将Widget本身作为参数传入

image.png

这里会去调用到父类中即RenderObjectElement

image.png

同样接着调用到父类即Element

image.png

在这里可以看到将外部传入的Widget对象进行了保存,同样在Element类中可以看到get方法

image.png

以上过程可以总结为Widget在创建过程中会去创建Element并且在其中保存了Widget对象,就如同文章刚开始的位置论证过context就是element一样,其中可以直接调用到WidgetWidget的存储过程便是这里

StatelessWidgetElement树

image.png

StatelessWidget中实现了createElement方法,与上面对Row的分析中相同,也是调用了一个构造方法,并将其本身当做参数传入

image.png

image.png

image.png

同样最终是在Element中对Widget进行了保存

StatefulWidgetElement树

现在对StatefulWidgetElement树来进行讨论

image.png

image.png

这里过程同上所以此处不再赘述,只不过会给state同时保存一份Widget

那么现在在此处打一个断点,这里应当是断在了MaterialApp

image.png

此处跨过此方法后可以发现下一步会执行mount方法

image.png

可以看到是在调用完createElement后会去调用mount方法,也就是说在创建一个新的Element后就要把它添加到Element树,那么就会调用mount这个方法

/// Add this element to the tree in the given slot of the given parent.
  ///
  /// The framework calls this function when a newly created element is added to
  /// the tree for the first time. Use this method to initialize state that
  /// depends on having a parent. State that is independent of the parent can
  /// more easily be initialized in the constructor.
  ///
  /// This method transitions the element from the "initial" lifecycle state to
  /// the "active" lifecycle state.
  ///
  /// Subclasses that override this method are likely to want to also override
  /// [update], [visitChildren], [RenderObjectElement.insertRenderObjectChild],
  /// [RenderObjectElement.moveRenderObjectChild], and
  /// [RenderObjectElement.removeRenderObjectChild].
  ///
  /// Implementations of this method should start with a call to the inherited
  /// method, as in `super.mount(parent, newSlot)`.
  @mustCallSuper
  void mount(Element? parent, Object? newSlot) {
    assert(_lifecycleState == _ElementLifecycle.initial);
    assert(widget != null);
    assert(_parent == null);
    assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
    assert(slot == null);
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) {
      // Only assign ownership if the parent is non-null. If parent is null
      // (the root node), the owner should have already been assigned.
      // See RootRenderObjectElement.assignOwner().
      _owner = parent.owner;
    }
    assert(owner != null);
    final Key? key = widget.key;
    if (key is GlobalKey) {
      owner!._registerGlobalKey(key, this);
    }
    _updateInheritance();
    attachNotificationTree();
  }

不同WidgetElementmount调用

Rowmount

通过上述分析得知在创建Element后要将它添加到Element树便要调用mount方法,那么现在来看看Row中是如何调用mount方法的

先找到MultiChildRenderObjectWidget中的creatElement方法

image.png

进入MultiChildRenderObjectElement中找到mount方法

image.png

这里又会调用到父类的mount方法,所以找到RenderObjectElement

image.png

继承于RenderObjectWidget的对象才可以创建RenderObject并加入RenderObject树,便是出自于这里了

StatelessWidgetmount

首先找到createElement方法

image.png

StatelessElement中未找到mount方法

image.png

进入其父类ComponentElement找到mount方法

image.png

进入_firstBuild方法

image.png

image.png

最终找到performRebuild方法

image.png

最终调用到StatelessElement中的build方法

image.png

这个方法其实是调用了外部继承于StatelessWidget的类中的build方法,同时外部build方法中的参数context其实就是这里的StatelessElement对象

StatefulWidgetmount

同样先找到createElement方法

image.png

这里调用createState方法,并且将Widget保存到state

image.png

找到父类中的mount方法

image.png

调用到StatefulElement中的_firstBuild方法

image.png

super调用到父类ComponentElement中的_firstBuild方法

image.png

调用Elementrebuild方法

image.png

调用到StatefulElementperformRebuild方法

image.png

super调用父类ComponentElementperformRebuild方法

image.png

调用StatefulElementbuild方法,并调用state中的build方法

image.png

总结:

Widget的声明周期:

  • StatelessWidget:

    • 构造方法
    • build方法
  • StatefulWidget:

    • Widget构造方法

    • WidgetcreateState方法

    • State构造方法

    • StateinitState方法

    • didChangeDependencies方法 (改变依赖关系):

      • 依赖(共享数据)的InheritedWidget发生变化后才会调用
    • Statebuild

      • 当调用setState方法会重新调用build进行渲染

        • setState内部会使用_element(本质就是context)调用markNeedsBuild方法
    • Widget销毁的时候调用Statedispose方法

Flutter的渲染原理:

  • Flutter的渲染流程中有三棵树:Widget🌲Element🌲Render🌲Flutter引擎渲染是针对于Render🌲中的对象进行渲染

  • 每个Widget对象创建出来都会创建一个Element对象:

    • 调用createElement方法,Element对象加入Element🌲都会调用mount方法

    • StatelessElement继承ComponentElement:

      • 主要调用build方法将自己(Element)也可以说是context传递出去
    • StatefulElement继承ComponentElement:

      • 调用createState方法创建state
      • Widget赋值给state
      • 调用statebuild方法并将自己(Element)作为参数传递出去
    • RenderElement主要是通过mount方法创建RenderObject对象