构建一个有验证判断的表单

应用程序通常会要求用户在文本框中输入信息。例如,我们可能正在开发一个应用程序,该应用程序就需要用户输入邮箱和密码登录。

为了让应用程序更为安全易用,我们通常都需要验证用户输入的信息是否有效。如果用户输入了正确的信息,就可以针对该信息进行后续处理。如果用户输入了错误的信息,就需要在相关的输入区域展示一条输入信息出错的提示,以便用户更正输入。

你可以通过以下步骤,在下面的例子中学习如何为表单中的文本输入框加入验证判断的功能:

  1. 创建表单 Form,并以 GlobalKey 作为唯一性标识

  2. 添加带验证逻辑的 TextFormField 到表单中

  3. 创建按钮以验证和提交表单

1. 创建表单 Form,并以 GlobalKey 作为唯一性标识

我们需要创建一个表单组件 Form 作为容器承载和验证多个表单域。

当我们创建表单 Form 的时候,需要提供一个 GlobalKeyGlobalKey 唯一标识了这个表单 Form,在后续的表单验证步骤中,也起到了关键的作用。

将表单创建为 StatefulWidget。这样只需要创建一次唯一的 GlobalKey<FormState>()。我们可以将它存储到一个变量,并在需要使用它的地方进行访问。

如果你的表单是 StatelessWidget,你就需要把这个 GlobalKey 放在 build 以外的 某个地方。因为我们不希望每次运行 build 方法时,都会生成一个新的 GlobalKey,这样会耗费大量资源。

import 'package:flutter/material.dart';

// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
  const MyCustomForm({super.key});

  @override
  MyCustomFormState createState() {
    return MyCustomFormState();
  }
}

// Define a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
  // Create a global key that uniquely identifies the Form widget
  // and allows validation of the form.
  //
  // Note: This is a `GlobalKey<FormState>`,
  // not a GlobalKey<MyCustomFormState>.
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Form(
      key: _formKey,
      child: const Column(
        children: <Widget>[
          // Add TextFormFields and ElevatedButton here.
        ],
      ),
    );
  }
}

2. 添加带验证逻辑的 TextFormField 到表单中

尽管在前面步骤中,已经创建出表单 Form 了,但我们此时还需要提供一个 TextFormField 让用户输入文本信息。 TextFormField 是遵循 material 设计风格的文本输入框,并且能够在输入验证不通过时显示错误提醒。

通过给 TextFormField 加入 validator() 函数可以验证输入是否正确。 validator 函数会校验用户输入的信息,如果信息有误,会返回包含出错原因的字符串 String。如果信息无误,则不返回。

在下面的实例中,我们会在 TextFormField 中加入一个 validator 验证函数,它的功能是判断用户输入的文本是否为空,如果为空,就返回「请输入文本」的友情提示。

TextFormField(
  // The validator receives the text that the user has entered.
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please enter some text';
    }
    return null;
  },
),

3. 创建按钮以验证和提交表单

在创建完表单以及文本框后,还需要提供一个按钮让用户提交表单。

当用户提交表单后,我们会预先检查表单信息是否有效。如果文本框有内容,表单有效,则会显示正确信息。如果文本框没有输入任何内容,表单无效,会在文本框区域展示错误提示。

ElevatedButton(
  onPressed: () {
    // Validate returns true if the form is valid, or false otherwise.
    if (_formKey.currentState!.validate()) {
      // If the form is valid, display a snackbar. In the real world,
      // you'd often call a server or save the information in a database.
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Processing Data')),
      );
    }
  },
  child: const Text('Submit'),
),

实现原理

为了验证表单,我们需要使用到步骤 1 中的 _formKey。使用 _formKey.currentState() 方法去访问 FormState,而 FormState 是在创建表单 Form 时 Flutter 自动生成的。

FormState 类包含了 validate() 方法。当 validate() 方法被调用的时候,会遍历运行表单中所有文本框的 validator() 函数。如果所有 validator() 函数验证都通过,validate() 方法返回 true。如果有某个文本框验证不通过,就会在那个文本框区域显示错误提示,同时 validate() 方法返回 false

交互式样例

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    const appTitle = 'Form Validation Demo';

    return MaterialApp(
      title: appTitle,
      home: Scaffold(
        appBar: AppBar(
          title: const Text(appTitle),
        ),
        body: const MyCustomForm(),
      ),
    );
  }
}

// Create a Form widget.
class MyCustomForm extends StatefulWidget {
  const MyCustomForm({super.key});

  @override
  MyCustomFormState createState() {
    return MyCustomFormState();
  }
}

// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
  // Create a global key that uniquely identifies the Form widget
  // and allows validation of the form.
  //
  // Note: This is a GlobalKey<FormState>,
  // not a GlobalKey<MyCustomFormState>.
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    // Build a Form widget using the _formKey created above.
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          TextFormField(
            // The validator receives the text that the user has entered.
            validator: (value) {
              if (value == null || value.isEmpty) {
                return 'Please enter some text';
              }
              return null;
            },
          ),
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 16),
            child: ElevatedButton(
              onPressed: () {
                // Validate returns true if the form is valid, or false otherwise.
                if (_formKey.currentState!.validate()) {
                  // If the form is valid, display a snackbar. In the real world,
                  // you'd often call a server or save the information in a database.
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('Processing Data')),
                  );
                }
              },
              child: const Text('Submit'),
            ),
          ),
        ],
      ),
    );
  }
}

想要了解更多关于如何获取这些值的内容,你可以参考 获取文本输入框的值 部分。