建立一個有驗證判斷的表單

應用程式通常會要求使用者在文字框中輸入訊息。例如,我們可能正在開發一個應用程式,該應用程式就需要使用者輸入郵箱和密碼登入。

為了讓應用程式更為安全易用,我們通常都需要驗證使用者輸入的訊息是否有效。如果使用者輸入了正確的訊息,就可以針對該訊息進行後續處理。如果使用者輸入了錯誤的訊息,就需要在相關的輸入區域展示一條輸入訊息出錯的提示,以便使用者更正輸入。

你可以透過以下步驟,在下面的例子中學習如何為表單中的文字輸入框加入驗證判斷的功能:

  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'),
            ),
          ),
        ],
      ),
    );
  }
}

想要瞭解更多關於如何獲取這些值的內容,你可以參考 獲取文字輸入框的值 部分。