一、StatefulWidget 与 StatelessWidget

现代化的前端框架都有一个共同的改变叫做 state,用来存储和变更状态(数据),并且变更能够反映到模板中

StatefulWidgetStatelessWidget 唯一的区别就在这里,如果只是渲染视图,状态变更不需要反映到模板中,则使用 StatelessWidget 即可,否则使用 StatefulWidget

StatelessWidget 的构建非常简单,在 Android Stuido 中可以通过 stless 快捷键直接生成一个 stateless wdiget:

91726-tzt3adwasqs.png

而基本的 StatelessWidget 中需要实现的是 build 方法:

class Demo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

StatefulWidget 复杂很多,Android Studio 中可以通过 stlful 快速创建一个 StatefulWidget:

50065-6ejkb5zuxx.png

一个 StatefulWidget 基本结构如下:

class Demo extends StatefulWidget {
  @override
  _DemoState createState() => _DemoState();
}

class _DemoState extends State<Demo> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

主要的业务逻辑及数据处理都在 State 中,因此创建完成之后,需要去 State 中实现 UI 和数据及生命周期相关操作。

二、StatefulWidget 的生命周期或方法

StatefulWidget 提供了几个不同的方法类似生命周期的处理方式,其中使用比较多的就是 initStatedispose

initState

initState State 对象创建的时候就会触发,官方文档描述如下:

Called when this object is inserted into the tree.
The framework will call this method exactly once for each State object it creates.

这个过程中我们能够进行一些初始化的操作,可以直接修改 state 的数据,类似 created 生命周期,比如:

  initState() {
    num = 2;
  }

dispose

Called when this object is removed from the tree permanently.

当 State 从 tree 中永久性 remove 的时候会触发

一般会移除一些事件监听什么的

deactive

Called when this object is removed from the tree.

deactivedispose 唯一的区别是 dispose 是真正的销毁掉 Widget 的时候,而 deactive 则不是,deactive 的场景往往是当前子树被移动到了树中的另一科子树,但是 state 还是在使用的,因此触发 deactive 能够保证一些资源延迟释放,提高性能

initState, didUpdateWidget,dispose 存在订阅的使用场景

  • In initState, subscribe to the object.
  • In didUpdateWidget unsubscribe from the old object and subscribe to the new one if the updated widget configuration requires replacing the object.
  • In dispose, unsubscribe from the object.

initState 中订阅一个对象

didUpdateWidget 中可以取消订阅旧对象,订阅新对象

dispose 中,彻底放弃对 object 的订阅

三、StatefulWidget 实现加减计数

Flutter 默认的 demo 就是个计数,不过用了 floatingButton,而且只能加,这里实现加减运算。

UI 上需要三部分组成:

  • 显示数字
  • 按钮
  • 按钮

功能上需要创建一个 state 存储数字,然后需要通过按钮触发的事件 setState 更新数字,然后会反映到模板中。

1、数字存储 及 点击事件更新 state

功能上实现如下:

  int num = 0;
  // num set state
  numSetState([bool flag = true]) {
    return () => setState(() {
      flag ? num++ : num--;
    });
  }

这里做了一个简单的逻辑,为了避免响应 + 和 - 写两个方法,通过执行一个函数,在返回一个匿名函数来收拢函数的数量,这种用法在 react 的参数传递中也比较常用。

数字变更必须放在 setState 中才能起作用,setState 接受一个函数作为参数,然后在函数中执行相关的逻辑,可以直接修改 num

dart 的优势在于对于对象和数组类型的 state,也可以直接修改,不需要像 js 一样还得重新赋值。

2、UI 实现

在 state 的 build 方法中实现 UI,和 stateless 其实一样

FlatButton 这个组件中,实现了 onPressed 事件,并且借助上面的 numSetState(flag) 能够指定是加还是减

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: <Widget>[
          SizedBox(height: 20),
          Text(
            num.toString(),
            style: TextStyle(
              color: Colors.pink,
              fontSize: 40,
            ),
          ),
          SizedBox(height: 20),
          Row(
            children: <Widget>[
              FlatButton(
                child: Text('+'),
                onPressed: numSetState(true),
                color: Theme.of(context).buttonColor,
              ),
              SizedBox(width: 10),
              FlatButton(
                child: Text('-'),
                onPressed: numSetState(false),
                color: Theme.of(context).buttonColor,
              )
            ],
            mainAxisAlignment: MainAxisAlignment.center,
          )
        ],
      ),
    );
  }

3、最终效果:

GIF.gif

四、示例代码

https://github.com/postbird/FlutterHelloWorldDemo/blob/master/demo1/lib/bak/main.24-StatefulWidget%20%E5%AE%9E%E7%8E%B0%E8%AE%A1%E6%95%B0%E5%99%A8.dart