Flutter_定义控件StatefulWidgets和StatelessWidget

Stateful(有状态) 和 stateless(无状态) widgets

    什么是重点?
    
    有些widgets是有状态的, 有些是无状态的
    
    如果用户与widget交互,widget会发生变化,那么它就是有状态的.
    
    widget的状态(state)是一些可以更改的值, 如一个slider滑动条的当前值或checkbox是否被选中.
    
    widget的状态保存在一个State对象中, 它和widget的布局显示分离。
    
    当widget状态改变时, State 对象调用setState(), 告诉框架去重绘widget.

stateless widget 没有内部状态. Icon、 IconButton, 和Text 都是无状态widget, 他们都是 StatelessWidget的子类。

stateful widget 是动态的. 用户可以和其交互 (例如输入一个表单、 或者移动一个slider滑块),或者可以随时间改变 (也许是数据改变导致的UI更新). Checkbox, Radio, Slider, InkWell, Form, and TextField 都是 stateful widgets, 他们都是 StatefulWidget的子类。

重点:
要创建一个自定义有状态widget,需创建两个类:StatefulWidget和State
状态对象包含widget的状态和build() 方法。
当widget的状态改变时,状态对象调用setState(),告诉框架重绘widget

StatefulWidget类
具有可变状态的小部件。

状态是(1)在构建窗口小部件时可以同步读取的信息,以及(2)在窗口小部件的生命周期内可能会更改的信息。这是小工具实施者的责任,以确保国家的及时通知当这种状态的改变,使用State.setState。

有状态窗口小部件是一个窗口小部件,它通过构建一个更具体地描述用户界面的其他窗口小部件来描述用户界面的一部分。构建过程以递归方式继续,直到用户界面的描述完全具体(例如,完全由RenderObjectWidget组成,其描述具体的RenderObject)。

当您描述的用户界面部分可以动态更改时(例如由于具有内部时钟驱动状态或依赖于某些系统状态),状态窗口小部件非常有用。对于仅依赖于对象本身中的配置信息以及窗口小部件膨胀的 BuildContext的组合,请考虑使用 StatelessWidget。

StatefulWidget实例本身是不可变的,并且将它们的可变状态存储在由createState方法创建的单独State对象中 ,或者存储在State订阅的对象中,例如Stream或ChangeNotifier对象,其引用存储在StatefulWidget的最终字段中本身。

框架在膨胀StatefulWidget时 调用createState,这意味着如果该窗口小部件已插入到多个位置的树中,则多个State对象可能与同一StatefulWidget关联。同样,如果StatefulWidget从树中移除,后来在树再次插入时,框架将调用createState再创建一个新的国家目标,简化的生命周期状态的对象。

如果StatefulWidget的创建者使用GlobalKey作为其 键,则StatefulWidget在从树中的一个位置移动到另一个位置时保持相同的State对象。由于具有GlobalKey的窗口小部件可以在树中的至多一个位置使用,因此使用GlobalKey的窗口小部件最多只有一个关联元素。当通过将与该窗口小部件关联的(唯一)子树从旧位置移植到新位置(而不是在该位置重新创建子树)时,框架利用此属性将全局键从树中的一个位置移动到另一个位置时利用此属性。新的位置)。与StatefulWidget关联的State对象与子树的其余部分一起被移植,这意味着State对象在新位置被重用(而不是被重新创建)。但是,为了有资格进行嫁接,必须将窗口小部件插入到从旧位置移除它的同一动画帧中的新位置。

性能考虑因素

StatefulWidget有两个主要类别。

首先是其中一个分配资源State.initState并在他们的处置State.dispose,但不依赖于InheritedWidget S或致电State.setState。这些小部件通常在应用程序或页面的根目录中使用,并通过ChangeNotifier, Stream或其他此类对象与子小部件进行通信。遵循这种模式的有状态小部件相对便宜(就CPU和GPU周期而言),因为它们构建一次然后永不更新。因此,它们可能有一些复杂和深刻的构建方法。

第二类是使用State.setState或依赖于 InheritedWidget的小部件。这些通常会在应用程序的生命周期内重建多次,因此最小化重建此类窗口小部件的影响非常重要。(他们也可以使用State.initState或 State.didChangeDependencies并分配资源,但重要的是他们重建。)

可以使用几种技术来最小化重建有状态窗口小部件的影响:

  • 将状态推到树叶上。例如,如果您的页面有一个滴答时钟,而不是将状态置于页面顶部并在每次时钟滴答时重建整个页面,则创建一个仅更新自身的专用时钟小部件。

  • 最小化构建方法及其创建的任何窗口小部件传递创建的节点数。理想情况下,有状态窗口小部件只会创建一个窗口小部件,而该窗口小部件将是一个RenderObjectWidget。(显然这并不总是实用的,但是小部件越接近这个理想,它就越有效率。)

  • 如果子树没有更改,请缓存表示该子树的窗口小部件,并在每次使用时重新使用它。对于要重新使用的窗口小部件而言,要比创建新的(但配置相同的)窗口小部件更有效。将有状态部分分解为带有子参数的小部件是执行此操作的常用方法。

  • const尽可能使用小部件。(这相当于缓存窗口小部件并重新使用它。)

  • 避免更改任何创建的子树的深度或更改子树中任何窗口小部件的类型。例如,不是返回包含在IgnorePointer中的子项或子项,而是始终将子窗口小部件包装在IgnorePointer中并控制IgnorePointer.ignoring 属性。这是因为更改子树的深度需要重建,布局和绘制整个子树,而只更改属性将需要对渲染树进行尽可能少的更改(例如,在IgnorePointer的情况下,没有布局或重绘是必要的)。

  • 如果由于某种原因必须更改深度,请考虑将子树的公共部分包装在具有GlobalKey的小部件中,该GlobalKey在有状态小部件的生命周期内保持一致。(如果没有其他小部件可以方便地分配密钥, KeyedSubtree小部件可能对此有用。)

实现一个自定义的有状态widget需要创建两个类:
    class MyApp extends StatelessWidget {
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
            //        title: 'Flutter Code Sample for material.Scaffold',
            theme: ThemeData(
              primarySwatch: Colors.blue,
            ),
            home: FavoriteWidget());
      }
    }
    1. 定义一个widget类,继承自StatefulWidget.
    class FavoriteWidget extends StatefulWidget {
      @override
      _FavoriteWidgetState createState() => new _FavoriteWidgetState();
    }
    
    2. 包含该widget状态并定义该widget build()方法的类,它继承自State.
    class _FavoriteWidgetState extends State {
      bool _isFavorited = true;
      int _favoriteCount = 41;
    
      void _toggleFavorite() {
        setState(() {
          // If the lake is currently favorited, unfavorite it.
          if (_isFavorited) {
            _favoriteCount -= 1;
            _isFavorited = false;
            // Otherwise, favorite it.
          } else {
            _favoriteCount += 1;
            _isFavorited = true;
          }
        });
      }
    
      @override
      Widget build(BuildContext context) {
        Widget content = Row(
          mainAxisSize: MainAxisSize.min,
          textDirection: TextDirection.ltr,
          children: [
            new Container(
              padding: new EdgeInsets.all(0.0),
              child: Center(
                child : IconButton(
                  icon: (_isFavorited
                      ? new Icon(Icons.star, textDirection: TextDirection.ltr)
                      : new Icon(Icons.star_border,
                          textDirection: TextDirection.ltr)),
                  color: Colors.red[500],
                  onPressed: _toggleFavorite,
                ),
              ),
            ),
            new SizedBox(
              width: 18.0,
              child: new Container(
                child: new Text(
                  '$_favoriteCount',
                  textDirection: TextDirection.ltr,
                ),
              ),
            ),
          ],
        );
        return Scaffold(body: content);
      }
}
IconButtonb必须要有一个Material Design的父类才能生效不然会报错

StatelessWidget类
一个不需要可变状态的小部件。

无状态窗口小部件是一个窗口小部件,它通过构建一个更具体地描述用户界面的其他窗口小部件来描述用户界面的一部分。构建过程以递归方式继续,直到用户界面的描述完全具体(例如,完全由RenderObjectWidget组成,其描述具体的RenderObject)。

当您描述的用户界面部分不依赖于对象本身的配置信息以及窗口小部件膨胀的BuildContext时,无状态窗口小部件非常有用。对于可以动态更改的组合,例如由于具有内部时钟驱动状态或依赖于某些系统状态,请考虑使用StatefulWidget。

性能考虑因素

无状态窗口小部件的构建方法通常仅在以下三种情况下调用:第一次将窗口小部件插入树中,窗口小部件的父窗口更改其配置时,以及何时依赖于更改的InheritedWidget。

如果窗口小部件的父级将定期更改窗口小部件的配置,或者它依赖于经常更改的继承窗口小部件,则优化构建方法的性能以保持流畅的呈现性能非常重要。

可以使用几种技术来最小化重建无状态窗口小部件的影响:

  • 最小化构建方法及其创建的任何窗口小部件传递创建的节点数。例如,考虑使用Align或 CustomSingleChildLayout,而不是精心安排Row,Column,Padding和SizedBox来以特别奇特的方式定位单个子节点。不要使用多个Container和装饰的复杂分层 来绘制恰当的图形效果,而应考虑单个CustomPaint小部件。

  • const尽可能使用小部件,并为小部件提供const构造函数,以便小部件的用户也可以这样做。

  • 考虑将无状态窗口小部件重构为有状态窗口小部件,以便它可以使用StatefulWidget中描述的一些技术,例如缓存子树的公共部分并在更改树结构时使用GlobalKey。

  • 如果由于使用InheritedWidget而可能经常重建窗口小部件 ,请考虑将无状态窗口小部件重构为多个窗口小部件,并将更改的树部分推送到树叶。例如,不是构建具有四个小部件的树,而是取决于主题的最内部小部件,考虑将构建最内部小部件的构建函数的部分分解为其自己的小部件,以便仅使用最内部的小部件需要在主题更改时重建。

你可能感兴趣的:(Flutter_定义控件StatefulWidgets和StatelessWidget)