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

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

在下面的例子中,将演示如何为表单中的文本输入框加入验证判断的功能。

步骤

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

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

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

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

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

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

  1. // Define a Custom Form Widget
  2. class MyCustomForm extends StatefulWidget {
  3. @override
  4. MyCustomFormState createState() {
  5. return MyCustomFormState();
  6. }
  7. }
  8. // Define a corresponding State class. This class will hold the data related to
  9. // the form.
  10. class MyCustomFormState extends State<MyCustomForm> {
  11. // Create a global key that will uniquely identify the Form widget and allow
  12. // us to validate the form
  13. //
  14. // Note: This is a `GlobalKey<FormState>`, not a GlobalKey<MyCustomFormState>!
  15. final _formKey = GlobalKey<FormState>();
  16. @override
  17. Widget build(BuildContext context) {
  18. // Build a Form widget using the _formKey we created above
  19. return Form(
  20. key: _formKey,
  21. child: // We'll build this out in the next steps!
  22. );
  23. }
  24. }

小提示:Using a GlobalKey is the recommended way to access a form. However, if youhave a more complex widget tree, you can use theForm.of method toaccess the form within nested widgets.

一般情况下,推荐使用 GlobalKey 来访问一个表单。嵌套组件且组件树比较复杂的情况下,可以使用 Form.of 方法访问表单。

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

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

那我们该如何去验证输入是否正确呢?只需要给 TextFormField 加入 validator 函数就可以了。validator 函数会校验用户输入的信息,如果信息有误,会返回包含出错原因的字符串 String。如果信息无误,则不返回。

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

  1. TextFormField(
  2. // The validator receives the text the user has typed in
  3. validator: (value) {
  4. if (value.isEmpty) {
  5. return 'Please enter some text';
  6. }
  7. },
  8. );

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

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

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

  1. RaisedButton(
  2. onPressed: () {
  3. // Validate will return true if the form is valid, or false if
  4. // the form is invalid.
  5. if (_formKey.currentState.validate()) {
  6. // If the form is valid, display a snackbar. In the real world, you'd
  7. // often want to call a server or save the information in a database
  8. Scaffold
  9. .of(context)
  10. .showSnackBar(SnackBar(content: Text('Processing Data')));
  11. }
  12. },
  13. child: Text('Submit'),
  14. );

实现原理

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

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

完整示例

  1. import 'package:flutter/material.dart';
  2. void main() => runApp(MyApp());
  3. class MyApp extends StatelessWidget {
  4. @override
  5. Widget build(BuildContext context) {
  6. final appTitle = 'Form Validation Demo';
  7. return MaterialApp(
  8. title: appTitle,
  9. home: Scaffold(
  10. appBar: AppBar(
  11. title: Text(appTitle),
  12. ),
  13. body: MyCustomForm(),
  14. ),
  15. );
  16. }
  17. }
  18. // Create a Form Widget
  19. class MyCustomForm extends StatefulWidget {
  20. @override
  21. MyCustomFormState createState() {
  22. return MyCustomFormState();
  23. }
  24. }
  25. // Create a corresponding State class. This class will hold the data related to
  26. // the form.
  27. class MyCustomFormState extends State<MyCustomForm> {
  28. // Create a global key that will uniquely identify the Form widget and allow
  29. // us to validate the form
  30. //
  31. // Note: This is a GlobalKey<FormState>, not a GlobalKey<MyCustomFormState>!
  32. final _formKey = GlobalKey<FormState>();
  33. @override
  34. Widget build(BuildContext context) {
  35. // Build a Form widget using the _formKey we created above
  36. return Form(
  37. key: _formKey,
  38. child: Column(
  39. crossAxisAlignment: CrossAxisAlignment.start,
  40. children: <Widget>[
  41. TextFormField(
  42. validator: (value) {
  43. if (value.isEmpty) {
  44. return 'Please enter some text';
  45. }
  46. },
  47. ),
  48. Padding(
  49. padding: const EdgeInsets.symmetric(vertical: 16.0),
  50. child: RaisedButton(
  51. onPressed: () {
  52. // Validate will return true if the form is valid, or false if
  53. // the form is invalid.
  54. if (_formKey.currentState.validate()) {
  55. // If the form is valid, we want to show a Snackbar
  56. Scaffold.of(context)
  57. .showSnackBar(SnackBar(content: Text('Processing Data')));
  58. }
  59. },
  60. child: Text('Submit'),
  61. ),
  62. ),
  63. ],
  64. ),
  65. );
  66. }
  67. }

表单验证示例