开发者

Flutter有状态组件StatefulWidget生命周期详解

目录
  • 1、StatefulWidget的背后
  • 2、StatefulWidget的生命周期
    • 2.1创建阶段
    • 2.2更新阶段
    • 2.3销毁阶段
  • 总结:

    1、StatefulWidget的背后

    flutter开发过程中,我们经常会用到两个组件StatelessWidget和StatefulWidget。前者为无状组件,后者为有状态组件,无状态组件通常在创建后内部的数据无法改变,而有状态组件可以维持内部的数据状态,适合动态组件使用。

    /// 无状态组件的定义
    class MyApp extends StatelessWidget {}
    /// 有状态状态组件的定义
    class MyApp extends StatefullWidget {
      @override
      State<StatefulWidget> createState() => _MyApp();
    }
    class _MyApp extends State<MyApp> {}
    

    定义无状态组件相对简单,只需要继承StatelessWidget即可,而有状态组件需要两个类来实现,首先是继承StatefullWidget,然后重写createState方法,返回State的实现类。

    刚始我不明白StatefullWidget为何要通过重写createState的方式来实现,后来通过对StatelessWidget的深入,我渐渐的理解了其中的用意。

    首先,StatelessWidget和StatefulWidget都的父类来自Widget,而Widget在定义过程中使用了Dart的注解@immutable。

    @immutable的作用根据官方的解释:被@immutable注解标明的类或者子类都必须是不可变的。

    也就是说,继承了StatelessWidget和StatefullWidget的组件都必须为常量组件,可以使用const修饰,而它的构造函数和成员属性需要是常量,不可变的。

    class MyApp extends StatelessWidget {
      /// 常量属性,不加final会警告
      final String? title;
      const MyApp({Key? key, this.title}) : super(key: key);
    }
    /// or
    class MyApp extends StatefulWidget {
      /// 常量属性,不加final会警告
      final String? title;
      const MyApp({Key? key, this.title}) : super(key: key);
      @override
      State<StatefulWidget> createState() => _MyApp();
    }
    /// 调用时可以加const
    const MyApp()
    

    StatelessWidget自然没什么问题,本身它就是无状态组件,创建出来内部数据不会改变,符合@immutable的定义。而StatefullWidget不同,作为有状态组件,需要维持自身的成员属性可变,不能是一个常量。那如何解决呢?就是通过State来保持状态,因为它并不继承Widget,不受@immutable的影响,内部成员可以定义成变量。

    还有个问题,为什么Widget非要使用@immutable来注释?

    这一切的出发点都是为了减少性能的损耗,提高Widget构建效率。因为常量定义的类,不会重复构建,可以大大提升运行速度,只要明白这一点,就能解了StatefulWidget这样设计的意义。

    2、StatefulWidget的生命周期

    StatefulWidget通过State来管理状态,同时也提供了相应的生命周期函数,如initState、didUpdateWidget、dispose等,我们只需要重写这些函数,StatefulWidget在执行过程中会在合适的时机调用。

    Flutter有状态组件StatefulWidget生命周期详解

    StatefulWidget的生命周期可以分为创建阶段、更新阶段和销毁阶段,下面我们结合一个示例来看下它的执行过程。

    /// StatefulWidget demo
    import 'package:flutter/material.dart';
    void main() {
      runApp(const MyApp());
    }
    class ColorInheritedwidget extends InheritedWidget {
      final Color color;
      const ColorInheritedWidget({
        Key? key,
        required this.color,
        required Widget child,
      }) : super(key: key, child: child);
      @override
      bool updateShouldNotify(covariant ColorInheritedWidget oldWidget) {
        return oldWidget.color != color;
      }
      static ColorInheritedWidget? of(BuildContext context) =>
          context.dependOnInheritedWidgetOfExactType<ColorInheritedWidget>();
    }
    class MyApp extends StatefulWidget {
      final String? title;
      const MyApp({Key? key, this.title}) : super(key: key);
      @override
      State<StatefulWidget> createState() {
        print('createState');
        return _MyApp();
      }
    }
    class _MyApp extends State<MyApp> {
      int value = 0;
      bool isDetach = false;
      Color color = Colors.red;
      @override
      void initState() {
        super.initState();
        print('initState');
      }
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print('didChangeDependencies');
      }
      @override
      void didUpdateWidget(covariant MyApp oldWidget) {
        super.didUpdateWidget(oldWidget);
        print('pythondidUpdateWidget');
      }
      @override
      void deactivate() {
        super.deactivate();
        print('deactivate');
      }
      @override
      void dispose() {
        sup编程er.dispose();
        print('deactivate');
      }
      @override
      Widget build(BuildContext context) {
        print('build');
        return ColorInheritedWidget(
          color: color,
          child: MaterialApp(
         js   home: Scaffold(
              appBar: AppBar(
                title: const Text('StatefulWidget demo'),
              ),
              body: Column(
                children: [
                  if (!isDetach) MyAppChild(value: value),
                  MaterialButton(
                    color: Colors.blue,
                    onPressed: () {
                      value++;
                      setState(() {});
                    },
                    child: const Text(js'更新value'),
                  ),
                  MaterialButton(
                    color: Colors.blue,
                    onPressed: () {
                      color = color == Colors.red ? Colors.yellow : Colors.red;
                      setState(() {});
                    },
                    child: const Text('更新color'),
                  ),
                  MaterialButton(
                    color: Colors.blue,
                    onPressed: () {
                      isDetach = !isDetach;
                      setState(() {});
                    },
                    child: Text(isDetach ? '添加' : '移除'),
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    class MyAppChild extends StatefulWidget {
      final int value;
      const MyAppChild({Key? key, required this.value}) : super(key: key);
      @override
      State<StatefulWidget> createState() {
        print('child-createState');
        return _MyAppChild();
      }
    }
    class _MyAppChild extends State<MyAppChild> {
      @override
      void initState() {
        super.initState();
        print('child-initState');
      }
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        print('child-d开发者_JAVA学习idChangeDependencies');
      }
      @override
      void didUpdateWidget(covariant MyAppChild oldWidget) {
        super.didUpdateWidget(oldWidget);
        print('child-didUpdateWidget');
      }
      @override
      void deactivate() {
        super.deactivate();
        print('child-deactivate');
      }
      @override
      void dispose() {
        super.dispose();
        print('child-dispose');
      }
      @override
      Widget build(BuildContext context) {
        print('child-build');
        return Container(
          width: 100,
          height: 100,
          color: ColorInheritedWidget.of(context)?.color,
          child: Text('${widget.value}'),
        );
      }
    }
    

    Flutter有状态组件StatefulWidget生命周期详解

    2.1创建阶段

    运行demo,首次渲染会打印出:

    flutter: createState

    flutter: initState

    flutter: didChangeDependencies

    flutter: build

    flutter: child-createState

    flutter: child-initState

    flutter: child-didChangeDependencies

    flutter: child-build

    从这里我们可以看出,StatefulWidget组件执行的顺序createState -> initState -> didChangeDependencies -> build。

    • createState

    Widget只是对组件信息的描述,而Element才是最终对Widget的实现。通过Element可以进行Widget的挂载和RenderObject的创建,最终实现UI的渲染。

    Flutter有状态组件StatefulWidget生命周期详解

    flutter首帧渲染的过程中,会不断向下遍历Widget树,通过cteateElememt创建Element实例,每创建一个Widget都会对应创建一个Element。例如创建StatefulWidget时会对应创建StatefulElement。

    abstract class StatefulWidget extends Widget {
      const StatefulWidget({ Key? key }) : super(key: key);
      @override
      StatefulElement createElement() => StatefulElement(this);
      /// ...    
    }
    

    StatefulElement的构造函数中,执行了StatefulWidget的createState方法,完成对State的调用。

    class StatefulElement extends ComponentElement {
      StatefulElement(StatefulWidget widget)
          : _state = widget.createState(),
            super(widget) {
                state._element = this;
                state._widget = widget;
            }
      /// ...
    }
    
    • init编程客栈State

    createState通过StatefulElement构造函数来创建,而initState在firstBuild方法中定义,firstBuild是首帧渲染的时候,通过StatefulElement实例的mount方法进行调用,firstBuild只会执行一次,也就是说initState只会在Widget首次创建次调用。

    通常我们会使用initState进行数据的初始化,也可以进行网络请求操作。

    /// StatefulElement
      @override
      void _firstBuild() {
        try {
          /// 调用initState
          final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
        } finally {
          /// ...
        }
        /// 调用didChangeDependencies
        state.didChangeDependencies();
        /// 调用父级ComponentElement类的_firstBuild,再往上到Element的rebuild方法,最后触发的是StatefulElement中的performRebuild方法。
        super._firstBuild();
      }
      @override
      void performRebuild() {
        /// _didChangeDependencies标记Element的依赖节点发生改变,didChangeDependencies会再次调用。
        if (_didChangeDependencies) {
          state.didChangeDependencies();
          _didChangeDependencies = false;
        }
        /// 调用ComponentElement中的performRebuild,最终触发StatefulElement的build方法
        super.performRebuild();
      }
      @override
      Widget build() => state.build(this);
    

    2.2更新阶段

    在State类中调用了setState方法,会解发组件update流程,它会对比新旧Element,将修改过的组件标记为脏元素。如果Widget依赖InheritedWidget的数据发现变化,会触发didChangeDependencies函数,接着会调用子组件的update方法,在StatefulElement的update方法中执行了didUpdateWidget,最后才执行rebuild进行build的调用,完成UI的更新。

    /// StatefulElement
      @protected
      @pragma('vm:prefer-inline')
      Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
        /// ...
        final Element newChild;
        if (child != null) {
          bool hasSameSuperclass = true;
          if (hasSameSuperclass && child.widget == newWidget) {
            newChild = child;
          } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
            /// 更新逻辑会调用child的update方法
            child.update(newWidget);
            newChild = child;
          }  
        } 
        return newChild;
      }
    
    /// StatefulElement
      @override
      void update(StatefulWidget newWidget) {
        super.update(newWidget);
        try {
          /// 调用state.didUpdateWidget
          final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
        }
        /// 重新rebuild深入遍历子组件
        rebuild();
      }
    

    在demo中,点击“更新value”按钮,会打印出:

    flutter: build

    flutter: child-didUpdateWidget

    flutter: child-build

    从这里可以看出父组件setState时,会先走自身的build再触发子组件的didUpdateWidget和build。

    2.3销毁阶段

    在demo中,点击“移除”按钮,会打印出:

    flutter: build

    flutter: child-deactivate

    flutter: child-dispose

    组件移除节点后会调用deactivate,如果该组件被移除节点,然后未被 插入到其他节点时,则会继续调用 dispose 永久移除,并释放组件资源。

    总结:

    1、StatefulWidget通过State来管理状态数据,目的是为了保持StatefulWidget可常量创建,减少性能的损耗,提高Widget构建效率。

    2、StatefulWidget创建阶段生命周期先执行顺序createState -> initState -> didChangeDependencies -> build。可以在initState进行数据初始化、网络请求操作。

    3、StatefulWidget更新阶段:build -> child didUpdateWidget -> child build。

    4、StatefulWidget销毁阶段:build -> child deactivate -> child dispose。

    以上就是Flutter有状态组件StatefulWidget生命周期详解的详细内容,更多关于Flutter StatefulWidget生命周期的资料请关注我们其它相关文章!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜