头图

🔑 0

我们所做出来的App,不能仅仅是一幅静止不动的画面!想一下你生活中的这些场景:

  • 手机健康App上的步数随着你的幸(huang)苦(dong)运(shou)动(bi)而增加;
  • 手机时钟App里的秒表计数随着时间的飞快流逝而改变;
  • 进度条慢慢延伸;

如果这些App是用Flutter编写成的,那么这些Widget的“状态”就在随着用户的操作而发生改变。

StatefulWidget就是有状态的Widget。它在程序运行时,状态被允许发生改变。

只有状态能够发生改变的StatefulWidget才能让页面不再一成不变。也就是说,要想编写出能够被用户操作的App,就必须要使用StatefulWidget

📘 目录

  1. 📜 Here's the code!
  2. 💠 理解StatefulWidgetState
  3. 🧱 当我们setState(() {});时,Flutter在幕后帮我们做了什么?

📜 1

Here's the code!

我们要实现这样的一个计数器:

image

我们实现了一个简单的计数器,画面中间的数字随着我们按动左下角的蓝色按钮而递增。

main.dart如下:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() {
    return MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  var _counter = 0;

  @override
  Widget build(BuildContext buildContext) {
    return MaterialApp(
      title: '计数器',
      home: Scaffold(
        appBar: AppBar(
          title: Text('计数器')
        ),
        body: Center(
          child: Text(
            '$_counter',
             style: TextStyle(fontSize: 30)
          )
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => setState(() => _counter += 1),
          child: Icon(Icons.add)
        ),
      )
    );
  }
}

代码来得太快,就像龙卷风?不要害怕,让我们一点一点剖析它。

💠 2

理解StatefulWidgetState

开头,我引入了package:flutter/material.dart,这里面包含了Flutter已经封装好的Material风格的Widget

import 'package: flutter/material.dart'

接下来,是经典的runApp(),这个时候把我编写好的MyApp(继承了StatefulWidget)的实例作为参数传入。

void main() => runApp(MyApp());

然后在MyApp内,我们会发现显示的具体内容并不是在这个类里面实现的!

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() {
    return MyAppState();
  }
}

在这个类里面,我们重写了一个createState()函数,然后这个函数返回了一个State<MyApp>,是用户定义的MyAppState类的实例!

然后MyAppState是何方神圣呢?

class MyAppState extends State<MyApp> {
  var _counter = 0;

  @override
  Widget build(BuildContext buildContext) {
    return MaterialApp(
      title: '计数器',
      home: Scaffold(
        appBar: AppBar(
          title: Text('计数器')
        ),
        body: Center(
          child: Text(
            '$_counter',
             style: TextStyle(fontSize: 30)
          )
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => setState(() => _counter += 1),
          child: Icon(Icons.add)
        ),
      )
    );
  }
}

可以看到了,MyAppState继承了State<MyApp>,而在这个类里面,我们重写了build()函数,并将这个Widget的内容写在了这里面。

所以,我们可以得知:

  • StatefulWidget里面需要重写createState()函数,这个函数返回一个State<StatefulWidget>的实例。
  • State<StatefulWidget>里面需要重写build()函数,这个函数返回一个Widget的实例。

但是,我们会发现,仅仅满足了这些,Widget的状态还不足以发生变化。回看刚才的源码,这里有一行:

onPressed: () => setState(() => _counter += 1)

floatingActionButtononPressed属性定义了这个按钮按完之后会触发什么动作,它的值是一个函数(在Dart内,函数是一等对象)。这个函数它会执行setState(() => _counter += 1)。那么,这个setState()是做什么的呢?

🧱 3

当我们setState(() {});时,Flutter在幕后帮我们做了什么?

  1. setState(fn)会先执行我们传入的函数fn
  2. 接下来,setState()会将这个组件标记为“脏的”。
  3. 下一帧时,Flutter会重新执行这个“脏”组件的build()函数,将它重新渲染一遍。

image

在上述计数器程序中setState(fn);的执行过程。动图为自制,转载请注明出处。图中组件树为了简洁明了,省略了AppBarIconWidget,只注明参与了这个过程的少数几个Widget

也就是说,setState({} ());才是本程序的点睛之笔

🏁 -1

总结

  • StatefulWidget就是有状态的Widget。它在程序运行时,状态被允许发生改变,要想编写出能够被用户操作的App,就必须要使用StatefulWidget
  • 我们实现了一个简单的计数器。
  • StatefulWidget里面需要重写createState()函数,这个函数返回一个State<StatefulWidget>的实例。
  • State<StatefulWidget>里面需要重写build()函数,这个函数返回一个Widget的实例。
  • 当我们执行setState(fn);时:

    1. setState(fn)会先执行我们传入的函数fn
    2. 接下来,setState()会将这个组件标记为“脏的”。
    3. 下一帧时,Flutter会重新执行这个“脏”组件的build()函数,将它重新渲染一遍。

它的用途是刷新一个组件的状态

image


SEREINMONO
1 声望0 粉丝

Keep It Simple, Stupid.