状态管理

状态管理

当 widget 被创建或者在 widget 的生命周期中有信息发生改变时所产生的信息叫做状态。要在 Flutter 中管理应用程序的状态,使用StatefulWidget 和 State 对象。

欲知更多关于 Flutter 的状态管理相关的内容,请参访状态管理文档 页面。

The StatelessWidget

StatelessWidget widget

StatelessWidget 在 Flutter 中是一个不需要状态改变的 widget,它没有内部的状态。

当你展现给用户的界面并不依赖其它任何配置信息并且使用BuildContext来解析 widget,则需要使用无状态 widget。

AboutDialog、CircleAvatar和 Text 是StatelessWidget 的子类,并且是很典型的无状态 widget。

// Flutter
import 'package:flutter/material.dart';

void main() => runApp(MyStatelessWidget(text: 'StatelessWidget Example to show immutable data'));

class MyStatelessWidget extends StatelessWidget {
  final String text;
  MyStatelessWidget({Key key, this.text}) : super(key: key);

  @override
 widget build(BuildContext context) {
    return Center(
      child: Text(
        text,
        textDirection: TextDirection.ltr,
      ),
    );
  }
}

在上面的例子中,你用到了 MyStatelessWidget 类的构造函数来传递 text。并且它被标记为 final。该类继承了 StatelessWidget,它包含不可变的数据。

无状态 widget 的 build 方法通常只有在三种情况下会被调用:

  • 当 widget 被插入到 widget 树中

  • 当 widget 的父 widget 改变了配置

  • 当所依赖的 InheritedWidget 发生了改变

The StatefulWidget

StatefulWidget widget

StatefulWidget 是携带状态变化的 widget。通过调用 setState 方法可以管理 StatefulWidget 的状态。当调用 setState 的时候,程序会通知 Flutter 框架有状态发生了改变,然后会重新运行 build 方法来更新应用的状态。

状态是在 widget 被创建期间可以被同步读取的信息,并且在 widget 的生命周期中会发生改变。实现该 widget 的时候要注意保证党状态发生改变的时候程序能够获得相应的提醒。当 widget 能够动态改变的时候,请使用 StatefulWidget。比如,某个 widget 会随着用户填写表单或者移动滑块的时候发生改变。亦或者随着数据源更新的时候发生改变。

Checkbox、Radio、Slider、InkWell、Form、和 TextField都是有状态的 widget,是StatefulWidget 的子类。

下面的示例代码声明了一个 StatefulWidget,需要实现 createState() 方法。该方法创建一个对象来管理 widget 的状态,也就是 _MyStatefulWidgetState。

class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

下面的状态类,_MyStatefulWidgetState,实现了 build() 方法。当状态发生改变的时候,比如说用户点击了开关按钮,这时 setState 就会被调用,并且将新的开关状态传进来。这就会使整体框架重构这个 widget。

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  bool showtext=true;
  bool toggleState=true;
  Timer t2;

  void toggleBlinkState(){
    setState((){
      toggleState=!toggleState;
    });
    var twenty = const Duration(milliseconds: 1000);
    if(toggleState==false) {
      t2 = Timer.periodic(twenty, (Timer t) {
        toggleShowText();
      });
    } else {
      t2.cancel();
    }
  }

  void toggleShowText(){
    setState((){
      showtext=!showtext;
    });
  }

  @override
 widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            (showtext
              ?(Text('This execution will be done before you can blink.'))
              :(Container())
            ),
            Padding(
              padding: EdgeInsets.only(top: 70.0),
              child: RaisedButton(
                onPressed: toggleBlinkState,
                child: (toggleState
                  ?( Text('Blink'))
                  :(Text('Stop Blinking'))
                )
              )
            )
          ],
        ),
      ),
    );
  }
}

StatefulWidget 和 StatelessWidget 的最佳实践是什么?

下面有一些设计原则供大家参考。

1. Determine whether a widget should be a StatefulWidget or a StatelessWidget

1. 确定一个 widget 应该是 StatefulWidget 还是 StatelessWidget

在 Flutter 中, widget 要么是有状态的,要么是无状态的。这取决于 widget 是否依赖状态的改变。

  • 如果一个 widget 发生了改变,而它所处的用户界面或者数据中断了 UI,那么该 widget 就是有状态的。

  • 如果一个 widget 是 final 类型或者 immutable 类型的,那么该 widget 是无状态的。

2. 确定哪个对象来控制 widget 的状态(针对 StatefulWidget )。

在 Flutter 中,有三种途径来管理状态:

  • widget 管理它的自身状态

  • 由其父 widget 管理 widget 状态

  • 通过混搭的方式

当决定了使用哪个途径后,要考虑下述的几个原则:

  • 如果状态信息是用户数据,比如 checkbox 是被勾选还是未被勾选,或者滑块的位置,那么父 widget 会很好的处理当前 widget 的状态。

  • 如果状态是和外观效果相关的,比如动画,那么 widget 自己会处理状态的变化。

  • 如果无法确定,那么父 widget 会处理子 widget 的状态。

3. 继承 StatefulWidget 和状态

MyStatefulWidget 类管理它自身的状态 - 它继承自 StatefulWidget,重写了 createState() 方法。该方法创建了 State 对象,同时框架会调用 createState() 方法来构建 widget。在这个例子中,createState() 方法创建了一个 _MyStatefulWidgetState 实例。下面的最佳实践中也实现了类似的方法。

class MyStatefulWidget extends StatefulWidget {
  MyStatefulWidget({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State<MyStatefulWidget> {

  @override
 widget build(BuildContext context) {
    ...
  }
}

4. 将 StatefulWidget 添加到 widget 树中

将你自定义的 StatefulWidget 通过应用程序的 build 方法添加到 widget 树中。

class MyStatelessWidget extends StatelessWidget {
  // This widget is the root of your application.

  @override
 widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyStatefulWidget(title: 'State Change Demo'),
    );
  }
}

State change on Android Android

State change on iOS iOS