Kodokon kodokon.com

交互性:按钮、StatefulWidget 与 setState

通过响应按钮点击并用 setState 更新显示,让你的界面活起来。

10 分钟 · 3 题

在 Kodokon 中打开本课

我们的界面很漂亮,但却是静止的:是时候来响应用户的操作了。Material 3 的标准按钮是 ElevatedButton。它接受两个关键参数:child,即它所显示的内容(通常是一个 Text),以及 onPressed,即你点击它时要运行的函数。像这样作为参数传入的函数被称为回调:你不会自己去调用它,而是由 Flutter 在恰当的时刻替你调用。() { ... } 语法会创建一个匿名函数,它没有名字,直接就地写出来。

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

Widget buildHelloButton() {
  return ElevatedButton(
    onPressed: () {
      debugPrint("Button pressed!");
    },
    child: const Text("Tap here"),
  );
}
一个每次点击都会向控制台写入内容的按钮

debugPrint 会向控制台写入内容……但屏幕本身却没有变化。为什么?因为一个 StatelessWidget无状态的 widget)是静止的:一旦构建完成,就再也不能改变。要显示一个会变化的值,你需要一个 StatefulWidget有状态的 widget)。所谓状态,就是在 widget 生命周期中可以改变的那组数据:一个计数器、一个勾选框、一段输入的文本……一个 StatefulWidget 总是和一个 State 类形影不离,后者保存着这些数据,并包含 build 方法。下面是入门时期的经典范例:计数器。

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

void main() {
  runApp(const CounterApp());
}

class CounterApp extends StatelessWidget {
  const CounterApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(useMaterial3: true),
      home: const CounterPage(),
    );
  }
}

class CounterPage extends StatefulWidget {
  const CounterPage({super.key});

  @override
  State<CounterPage> createState() =>
      _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              "Counter: $count",
              style: const TextStyle(fontSize: 32),
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  count = count + 1;
                });
              },
              child: const Text("+1"),
            ),
          ],
        ),
      ),
    );
  }
}
计数器:一个完整的 StatefulWidget

这个机制分为三步。1) count 变量存在于 _CounterPageState 中:这就是状态。2) 每次点击时,回调都会调用 setState:这个方法会修改状态并告诉 Flutter 有东西发生了变化。3) 于是 Flutter 会重新运行 build,用 count 的新值重新绘制屏幕。请记住这套理念:你从不直接改变屏幕,你改变的是数据,而显示则交给 Flutter 来负责。

知识检测

确认你已牢记本课的重点内容。

  1. ElevatedButton 的 onPressed 参数有什么用?
    • 设置按钮上显示的文本
    • 提供按钮被点击时运行的函数
    • 选择按钮的颜色
  2. 什么时候应该使用 StatefulWidget 而不是 StatelessWidget?
    • 当显示依赖于随时间变化的数据时
    • 当 widget 包含超过三个子节点时
    • 当你想让应用启动得更快时
  3. 如果你改变了 count 却没有调用 setState,会发生什么?
    • 应用立即崩溃
    • 变量在内存中改变了,但屏幕没有更新
    • Flutter 会自动替你调用 setState