Hôm nay, chúng ta sẽ làm quen với Form – giúp gom nhiều TextField
lại với nhau và kiểm tra dữ liệu trước khi lưu hay gửi.
🎯 Mục tiêu hôm nay
- Sử dụng widget
Form
vàTextFormField
- Kiểm tra dữ liệu nhập (validation)
- Kết hợp nhiều trường nhập trong cùng một form
TextField vs TextFormField – Khác nhau gì?
Trước khi bắt đầu với Form
, chúng ta cần phân biệt một chút giữa hai widget nhập liệu trong Flutter:
Đặc điểm | TextField | TextFormField |
---|---|---|
Dùng để | Nhập dữ liệu đơn lẻ | Nhập dữ liệu trong một Form |
Hỗ trợ validation | ❌ Không hỗ trợ sẵn | ✅ Có thể dùng validator dễ dàng |
Thường dùng khi... | Chỉ cần nhập liệu không cần kiểm tra | Khi cần kiểm tra dữ liệu (form đăng ký...) |
Kết hợp với Form |
Không | Được thiết kế để dùng với Form |
Ví dụ:
// TextField – không có validate
TextField( controller: _nameController, decoration: InputDecoration(labelText: 'Tên'),
); // TextFormField – có thể validate
TextFormField( controller: _nameController, decoration: InputDecoration(labelText: 'Tên'), validator: (value) { if (value == null || value.isEmpty) { return 'Vui lòng nhập tên'; } return null; },
);
➡️ Nói ngắn gọn: Khi chúng ta cần kiểm tra dữ liệu người dùng nhập, hãy dùng TextFormField
trong Form. Còn nếu chỉ nhập đơn giản (ví dụ ô tìm kiếm), thì TextField
là đủ.
Thực hành: Tạo form đăng ký đơn giản
Chúng ta sẽ xây dựng một form với 2 ô nhập:
- Tên người dùng (bắt buộc nhập)
- Email (bắt buộc nhập và phải đúng định dạng email)
Khi bấm nút "Đăng ký", nếu hợp lệ thì hiển thị thông báo đăng ký thành công.
import 'package:flutter/material.dart'; class SimpleFormExample extends StatefulWidget { _SimpleFormExampleState createState() => _SimpleFormExampleState();
} class _SimpleFormExampleState extends State<SimpleFormExample> { final _formKey = GlobalKey<FormState>(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _emailController = TextEditingController(); void dispose() { _nameController.dispose(); _emailController.dispose(); super.dispose(); } void _submitForm() { if (_formKey.currentState!.validate()) { final name = _nameController.text; showDialog( context: context, builder: (_) => AlertDialog( content: Text('Chào mừng $name! Đăng ký thành công 🎉'), ), ); } } Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Form Đăng ký')), body: Padding( padding: const EdgeInsets.all(20), child: Form( key: _formKey, child: Column( children: [ TextFormField( controller: _nameController, decoration: InputDecoration(labelText: 'Tên'), validator: (value) { if (value == null || value.isEmpty) { return 'Vui lòng nhập tên'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: _emailController, decoration: InputDecoration(labelText: 'Email'), validator: (value) { if (value == null || value.isEmpty) { return 'Vui lòng nhập email'; } final emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+'); if (!emailRegex.hasMatch(value)) { return 'Email không hợp lệ'; } return null; }, ), SizedBox(height: 24), ElevatedButton( onPressed: _submitForm, child: Text('Đăng ký'), ), ], ), ), ), ); }
}
- TextFormField nên được gói bên trong Form để quản lý dữ liệu hiệu quả.
- Mỗi ô nhập có thể có validator để kiểm tra nội dung.
GlobalKey<FormState>
giúp truy cập và kiểm tra toàn bộ trạng thái của form, ví dụ như validate().
Trong các ứng dụng thực tế, form
có thể phức tạp hơn nhiều với nhiều trường nhập và các loại validation
khác nhau. Chúng ta cũng có thể tùy chỉnh giao diện thông báo lỗi để phù hợp với thiết kế của ứng dụng bằng cách sử dụng thuộc tính errorText trong InputDecoration.
Lời kết
Qua bài học ngày hôm nay, chúng ta đã biết cách sử dụng Form và TextFormField để kiểm tra dữ liệu người dùng nhập vào. Điều này cực kỳ quan trọng khi xây dựng các form đăng ký, đăng nhập, liên hệ... Sắp tới, mình sẽ thử làm một mini app có sử dụng form, để luyện tập thực tế luôn 😄
Lời cuối bài xin cảm ơn mọi người đã đọc đến đây! Nếu mọi người cũng đang tìm hiểu Flutter thì mình chúc chúng ta kiên trì và vững bước mỗi ngày. Hẹn gặp lại mọi người ở bài viết ngày mai nhé! ✌️🚀