Tác giả Đoàn Ngọc Tú là giảng viên khóa Lộ trình Flutter
Thông tin chi tiết khóa học tại : https://flutter.techmaster.vn/

Giới thiệu

Rất nhiều ứng dụng thường yêu cầu người dùng nhập liệu. Ví dụ, bạn có thể yêu cầu người dùng đăng nhập với một địa chỉ email và mật khẩu.

Để ứng dụng bảo mật và dễ sử dụng, ta cần kiểm tra thông tin mà người dùng cung cấp xem thông tin đó khả dụng hay không. Nếu người dùng điền thông tin vào form nhập liệu đầy đủ và chính xác, lúc đó ta bắt đầu tiến trình xử lý thông tin. Nếu người dùng nhập sai, ta cần hiển thị một thông báo cho người dùng biết họ đang làm sai điều gì.

Trong ví dụ ở bài viết này, chúng ta sẽ học được cách để thêm việc kiểm tra tính khả dụng và đúng đắn của 1 form nhập liệu có 1 input text qua những bước sau:

  1. Tạo Form với một GlobalKey.
  2. Thêm TextFormField với logic kiểm tra tính khả dụng.
  3. Tạo 1 button để validate và submit form.

1. Tạo 1 Form với GlobalKey

Đầu tiên, tạo một Form. Form widget hoạt động giống như 1 container có tác dụng gom nhóm và validate nhiều form field.

Khi tạo một form, cần cung cấp 1 GlobalKey. Key này là duy nhất, nhằm xác định Form, và cho phép validate form trong bước sau.

import 'package:flutter/material.dart';

// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: 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 Scaffold(
      appBar: AppBar(
        title: Text('Form'),
      ),
      body: Form(
        key: _formKey,
        child: Column(
          children: <Widget>[
            // Add TextFormFields and ElevatedButton here.
          ],
        ),
      ),
    );
}
}

Ta có kết quả như ảnh sau:

form 1

Sử dụng GlobalKey là cách được khuyến nghị để truy cập vào 1 form, tuy nhiên nếu bạn có một widget tree phức tạp hơn, bạn có thể sử dụng phương thức Form.of() để truy cập form mà không cần thông qua widget.

2. Thêm 1 TextFormField với logic xác nhận

Mặc dù chúng ta đã có Form, nhưng chưa có cách nào để người dùng nhập dữ liệu. Và ta cần tới TextFormField. TextFormField widget tạo ra một text field dạng material design và có thể hiển thị lỗi khi dữ liệu sai.

Xác thực đầu vào bằng cách cung cấp một hàm validator() cho TextFormField. Nếu thông tin đầu vào của người dùng không hợp lệ, hàm validator() sẽ trả về một chuỗi chứa thông báo lỗi. Nếu không có lỗi, validator phải trả về null.

Đối với ví dụ này, hãy tạo validator để đảm bảo TextFormField không trống. Nếu nó trống, hãy trả về một thông báo lỗi.

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

3. Tạo một nút để xác thực và gửi biểu mẫu

Bây giờ bạn đã có một form với trường để nhập liệu, hãy cung cấp một button mà người dùng có thể nhấn để gửi thông tin.

Khi người dùng cố gắng gửi biểu mẫu, hãy kiểm tra xem biểu mẫu có hợp lệ không. Nếu đúng, hiển thị thông báo thành công. Nếu không (trường nhập liệu không có nội dung), hãy hiển thị thông báo lỗi.

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'),
),
form3

Giải thích cách hoạt động

Để xác thực biểu mẫu, chúng ta có thể sử dụng _formKey đã tạo ở bước 1. Bạn có thể sử dụng phương thức _formKey.currentState() để truy cập FormState, được Flutter tạo tự động khi xây dựng Form.

Lớp FormState chứa phương thức validate(). Khi phương thức validate() được gọi, nó sẽ chạy hàm validator() cho mỗi trường nhập liệu trong biểu mẫu. Nếu mọi thứ đều ổn, phương thức validate() trả về true. Nếu bất kỳ trường văn bản nào có lỗi, phương thức validate() sẽ xây dựng lại biểu mẫu để hiển thị bất kỳ thông báo lỗi nào và trả về false.

Một ví dụ tổng quan

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: 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({Key? key}) : super(key: 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.0),
            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'),
            ),
          ),
        ],
      ),
    );
}
}

Kết quả sau cùng sẽ như sau:

form5form error