本篇依据Flutter 3.16.4,Dart 3.2.3版别

Flutter 3.16.4 • channel stable • github.com/flutter/flu…

Framework • revision 2e9cb0aa71 (3 days ago) • 2023-12-11 14:35:13 -0700

Engine • revision 54a7145303

Tools • Dart 3.2.3 • DevTools 2.28.4

本篇为Flutter基建的第七篇文章,主要介绍Flutter中StatelessWidget和StatefulWidget运用以及根底类Widget相关常识,希望本篇文章能够帮助咱们了解和熟悉Widget的基本常识,下面一同进入文章的内容吧~

Flutter基建 - 寸步不离的State**Widget

Flutter系列文章

Flutter基建 – Dart根底类型

Flutter基建 – Dart办法和类

Flutter基建 – 文本组件

Flutter基建 – 按钮全解析

Flutter基建 – 布局组件全面解析

Flutter基建 – 离不开的列表组件

Flutter基建 – 寸步不离的State**Widget

Widget基类

在这之前假如你观察过StatelessWidget和StatefulWidget的源码就会发现,其实它俩都是承继自Widget抽象类。

abstract class StatelessWidget extends Widget
abstract class StatefulWidget extends Widget

二者的差异在于StatelessWidget表明的是一个无状况的Widget,它不能够更新Widget内部状况,而StatefulWidget能够经过setState来更新Widget的状况,这儿暂时了解这些基本概念,先进入Widget内部看看。

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });
  final Key? key;
  @protected
  @factory
  Element createElement();
  @override
  String toStringShort() {
    final String type = objectRuntimeType(this, 'Widget');
    return key == null ? type : '$type-$key';
  }
  @override
  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
    super.debugFillProperties(properties);
    properties.defaultDiagnosticsTreeStyle = DiagnosticsTreeStyle.dense;
  }
  @override
  @nonVirtual
  bool operator ==(Object other) => super == other;
  @override
  @nonVirtual
  int get hashCode => super.hashCode;
  static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}

Widget抽象类的源码很简略,它承继自DiagnosticableTree,此Tree仅仅用于调试所用,这儿就不过多介绍,仍是介绍Widget内部的几个办法。

  • createElement()办法用于创立Widget对应的Element目标,Flutter会依据详细的Element生成Render树,Render树便是烘托界面的元素;一个Widget对应一个Element,StatelessWidget创立的是StatelessElement目标,StatefulWidget创立的是StatefulElement目标,关于Widget、Element和Render三者的联系比较杂乱,后面会写一篇文章详细介绍它们三个的联系和效果。
  • toStringShort()办法回来的是当时Widget的简略的信息。
  • debugFillProperties()办法用于添加一些调试属性,一般情况下很少用到。
  • canUpdate()办法效果仍是比较重要的,它用于判别重建时Widget是否需求更新,默认是比照两个Widget的runtimeType和key是否相同。

Widget源码不杂乱,详细杂乱的是Widget和Element、Render的联系,这儿先暂时了解这么多,更深化的常识咱们后续渐渐了解。

StatelessWidget

根底源码

abstract class StatelessWidget extends Widget {
  const StatelessWidget({ super.key });
  @override
  StatelessElement createElement() => StatelessElement(this);
  @protected
  Widget build(BuildContext context);
}

StatelessWidget源码中完成了父类的createElement()办法,回来了一个StatelessElement目标,而且将本身传入StatelessElement结构办法傍边,然后界说了一个build()办法,用于创立详细的子Widget元素。

接下来咱们再看看怎么运用StatelessWidget来展现界面UI:

实际运用

class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({super.key});
  @override
  Widget build(BuildContext context) {
    return buildScaffold(
      context,
      const Center(
        child: Text('MyStatelessWidget'),
      ),
    );
  }
}

Flutter基建 - 寸步不离的State**Widget

运用StatelessWidget时,咱们只需求复写build()办法即可,然后在此办法内部将界面Widget回来,这样就能够完成一个无状况的Widget。

假如咱们当时的页面需求信息的更新,那么StatelessWidget就不再适用,此刻咱们需求选择StatefulWidget来完成界面元素的结构,下面咱们接着看看有状况的Widget是怎么运用。

StatefulWidget

根底源码

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ super.key });
  @override
  StatefulElement createElement() => StatefulElement(this);
  @protected
  @factory
  State createState();
}

StatefulWidget源码中也是完成了父类的createElement()办法,回来了一个StatefulElement目标,而且将本身传入StatefulElement结构办法傍边,和StatelessWidget不同的是,它并没有界说build()办法用于创立界面Widget,而是界说了一个createState()办法,用于创立一个State目标,此目标便是用于保存当时Widget中相关的状况信息,更新状况信息也是经过State来完成。

了解了StatefulWidget根底源码之后,咱们紧接着来看看怎么运用StatefulWidget和State展现一个可修改状况信息的Widget。

实际运用

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({super.key});
  @override
  State<StatefulWidget> createState() {
    return _MyState();
  }
}
class _MyState extends State<MyStatefulWidget> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter组件'),
      ),
      body: Center(
        child: Text('count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            ++count;
          });
        },
        child: const Icon(Icons.favorite),
      ),
    );
  }
}

Flutter基建 - 寸步不离的State**Widget

经过上述的代码能够看到StatefulWidget中逻辑变得很少了,仅仅复写了createState()办法,回来了_MyState目标而已,详细的子Widget怎么创立,Widget的状况信息都下放到_MyState傍边了,_MyState承继自State类,然后复写了build()办法,创立界面Widget,而且在内部还界说了一个count变量,用于中心Text显现的文本数据,然后在FloatingActionButton的点击事情中经过setState()办法更新count值,随之Text文本也会跟随着count改动而及时更新。

StatefulWidget能够依靠State目标对内部的状况信息做出更新操作,这也是它和StatelessWidget最大的不同,假如你开发的界面需求界面更新,那么选择StatefulWidget来编写界面是名副其实的。

下面咱们再来看看怎么经过State感知当时Widget的生命周期改动。

生命周期

class _MyState extends State<MyStatefulWidget> {
  int count = 0;
  @override
  Widget build(BuildContext context) {
    debugPrint('build');
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter组件'),
      ),
      body: Center(
        child: Text('count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            ++count;
          });
        },
        child: const Icon(Icons.favorite),
      ),
    );
  }
  @override
  void initState() {
    super.initState();
    debugPrint('initState');
  }
  @override
  void didUpdateWidget(covariant MyStatefulWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    debugPrint('didUpdateWidget');
  }
  @override
  void deactivate() {
    super.deactivate();
    debugPrint('deactivate');
  }
  @override
  void activate() {
    super.activate();
    debugPrint('activate');
  }
  @override
  void reassemble() {
    super.reassemble();
    debugPrint('reassemble');
  }
  @override
  void dispose() {
    super.dispose();
    debugPrint('dispose');
  }
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    debugPrint('didChangeDependencies');
  }
}

上述代码仅仅简单的在各个生命周期中加了debug信息的打印,下面咱们逐一来看下每个生命周期是怎么调用的。

当界面第一次被翻开时:

initState
didChangeDependencies
build

咱们会发现当界面第一次被翻开时,生命周期的改动是initStaste -> didChangeDependencies -> build

当界面退出时:

deactivate
dispose

界面从翻开状况变为退出状况时,生命周期的改动是deactivate -> dispose

当咱们点击Android Studio的hot reload时:

reassemble
build

当咱们点击FloatingActionButton将count值改动时:

build

此刻之后调用build()办法进行界面的重绘。

分析到这停止,咱们能够大致将State的生命周期给梳理成一个框架:

Flutter基建 - 寸步不离的State**Widget

上面这个流程图就将State的从进入界面到退出界面,中心包括hot reload动作和initState动作的生命周期就完整的表述出来了,假如咱们有疑问不了解的当地,欢迎在评论区沟通交流

不知道看到这儿之后,咱们有没有发现,activate和didUpdateWidget这两个办法如同自始至终都没有被调用过,莫非它们二者不会在生命周期的过程中出现出来么?不会的,仅仅它们两个比较特殊,有对应的触发条件,下面咱们一个一个来解释下。

didUpdateWidget表明每当Widget装备改动时都会被调用,那么咱们是否有种疑问,上面调用setState办法时count不是现已改动了么,为啥没有被调用呢,didUpdateWidget调用机遇是父Widget的装备改动需求重建时,子Widget会调用此didUpdateWidget办法进行重建,接下来咱们经过代码验证下。

咱们将_MyState中原先Center中Text去掉,换成MyFulWidgetChild()组件,此组件中复写didUpdateWidget办法。

class _ChildState extends State<MyFulWidgetChild> {
  @override
  Widget build(BuildContext context) {
    return const Text('Child');
  }
  @override
  void didUpdateWidget(covariant MyFulWidgetChild oldWidget) {
    super.didUpdateWidget(oldWidget);
    debugPrint('child didUpdateWidget');
  }
}

此刻咱们点击_State中FloatingActionButton改动count值时,_ChildState的didUpdateWidget办法就会被调用了,看到这咱们应该就会理解了此生命周期的触发的详细条件了。

还剩最终一个activate生命周期,此生命周期依据注释能够得出它的触发条件是:当Widget经过deactivate被移出界面时同时又被加入到另外一个界面它才会被调用,此生命周期日常开发中基本不会运用。

写在最终

本篇文章简单概括性的介绍了Widget和StatelessWidget、StatefulWidget的相关常识,希望能够帮助咱们了进一步了解和熟悉StatelessWidget和StatefulWidget的相关常识,后续会按部就班逐渐触摸Flutter更多的常识。

我是Taonce,假如觉得本文对你有所帮助,帮忙关注、点赞或者收藏三连一下,谢谢~