Skip to main content

2 posts tagged with "Form"

View All Tags

Flutter: Button, TextField và Form widgets

· 4 min read

Button, TextField và Form là những widget quan trọng để tạo giao diện tương tác trong ứng dụng Flutter. Bài viết này sẽ hướng dẫn bạn cách sử dụng chúng một cách hiệu quả.

1. Button Widgets

Flutter cung cấp nhiều loại button khác nhau để phù hợp với các nhu cầu khác nhau.

Button Widget Examples

1.1. ElevatedButton

ElevatedButton(
onPressed: () {
// Xử lý sự kiện khi button được nhấn
},
child: const Text('Elevated Button'),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
),
)

1.2. TextButton

TextButton(
onPressed: () {
// Xử lý sự kiện khi button được nhấn
},
child: const Text('Text Button'),
style: TextButton.styleFrom(
foregroundColor: Colors.blue,
),
)

1.3. IconButton

IconButton(
onPressed: () {
// Xử lý sự kiện khi button được nhấn
},
icon: const Icon(Icons.favorite),
color: Colors.red,
)

1.4. OutlinedButton

OutlinedButton(
onPressed: () {
// Xử lý sự kiện khi button được nhấn
},
child: const Text('Outlined Button'),
style: OutlinedButton.styleFrom(
side: const BorderSide(color: Colors.blue),
),
)

2. TextField Widget

TextField widget được sử dụng để nhận input từ người dùng.

TextField Widget Examples

2.1. Basic TextField

TextField(
decoration: InputDecoration(
labelText: 'Username',
hintText: 'Enter your username',
prefixIcon: const Icon(Icons.person),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
)

2.2. Password TextField

TextField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
prefixIcon: const Icon(Icons.lock),
suffixIcon: IconButton(
icon: const Icon(Icons.visibility),
onPressed: () {
// Toggle password visibility
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
)

2.3. Number TextField

TextField(
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'Age',
hintText: 'Enter your age',
prefixIcon: const Icon(Icons.numbers),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
)

3. Form Widget

Form widget được sử dụng để quản lý và validate nhiều TextField cùng lúc.

Form Widget Examples

3.1. Basic Form

final _formKey = GlobalKey<FormState>();

Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email';
}
return null;
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Process form data
}
},
child: const Text('Submit'),
),
],
),
)

3.2. Form với Custom Validation

TextFormField(
decoration: const InputDecoration(
labelText: 'Phone Number',
hintText: 'Enter your phone number',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your phone number';
}
if (!RegExp(r'^\d{10}$').hasMatch(value)) {
return 'Please enter a valid 10-digit phone number';
}
return null;
},
)

4. Best Practices

4.1. Button

  • Sử dụng đúng loại button cho từng trường hợp
  • Thêm loading state cho button khi cần
  • Xử lý disable state khi cần thiết
  • Sử dụng const constructor khi có thể

4.2. TextField

  • Luôn có label hoặc hint text
  • Sử dụng prefix/suffix icon khi cần
  • Xử lý keyboard type phù hợp
  • Validate input khi cần thiết

4.3. Form

  • Sử dụng Form widget để quản lý nhiều field
  • Implement validation logic rõ ràng
  • Hiển thị error message rõ ràng
  • Xử lý form submission một cách an toàn

Kết luận

Button, TextField và Form là những widget cơ bản nhưng rất quan trọng trong Flutter. Việc hiểu rõ cách sử dụng chúng sẽ giúp bạn tạo ra giao diện người dùng tương tác hiệu quả.


Tài liệu tham khảo:

Flutter: Quản lý form và validation

· 8 min read

Form là một phần quan trọng trong hầu hết các ứng dụng. Bài viết này sẽ hướng dẫn cách quản lý form và thực hiện validation trong Flutter.

Form Validation

1. Form Widget

1.1. Cấu trúc cơ bản

class MyForm extends StatefulWidget {
@override
_MyFormState createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
final _formKey = GlobalKey<FormState>();

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(
labelText: 'Username',
hintText: 'Enter your username',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your username';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Xử lý form
}
},
child: Text('Submit'),
),
],
),
);
}
}

1.2. FormField Widget

class CustomFormField extends FormField<String> {
CustomFormField({
Key? key,
required String label,
required String? Function(String?) validator,
void Function(String?)? onSaved,
}) : super(
key: key,
validator: validator,
onSaved: onSaved,
builder: (FormFieldState<String> state) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(label),
TextField(
onChanged: (value) {
state.didChange(value);
},
decoration: InputDecoration(
errorText: state.errorText,
),
),
],
);
},
);
}

2. Validation

2.1. Validation cơ bản

class ValidationForm extends StatefulWidget {
@override
_ValidationFormState createState() => _ValidationFormState();
}

class _ValidationFormState extends State<ValidationForm> {
final _formKey = GlobalKey<FormState>();
String _email = '';
String _password = '';

String? _validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!value.contains('@')) {
return 'Please enter a valid email';
}
return null;
}

String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
validator: _validateEmail,
onSaved: (value) => _email = value ?? '',
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
validator: _validatePassword,
onSaved: (value) => _password = value ?? '',
),
ElevatedButton(
onPressed: _submitForm,
child: Text('Submit'),
),
],
),
);
}

void _submitForm() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Xử lý form
}
}
}

2.2. Validation nâng cao

class AdvancedValidationForm extends StatefulWidget {
@override
_AdvancedValidationFormState createState() => _AdvancedValidationFormState();
}

class _AdvancedValidationFormState extends State<AdvancedValidationForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();

@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}

String? _validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
}

String? _validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
if (!value.contains(RegExp(r'[A-Z]'))) {
return 'Password must contain at least one uppercase letter';
}
if (!value.contains(RegExp(r'[0-9]'))) {
return 'Password must contain at least one number';
}
return null;
}

String? _validateConfirmPassword(String? value) {
if (value != _passwordController.text) {
return 'Passwords do not match';
}
return null;
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: _validateEmail,
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: _validatePassword,
),
TextFormField(
controller: _confirmPasswordController,
decoration: InputDecoration(
labelText: 'Confirm Password',
prefixIcon: Icon(Icons.lock_outline),
),
obscureText: true,
validator: _validateConfirmPassword,
),
ElevatedButton(
onPressed: _submitForm,
child: Text('Register'),
),
],
),
);
}

void _submitForm() {
if (_formKey.currentState!.validate()) {
// Xử lý form
}
}
}

3. Form với Provider

3.1. Form Provider

class FormProvider extends ChangeNotifier {
String _email = '';
String _password = '';
bool _isLoading = false;
String? _error;

String get email => _email;
String get password => _password;
bool get isLoading => _isLoading;
String? get error => _error;

void updateEmail(String value) {
_email = value;
notifyListeners();
}

void updatePassword(String value) {
_password = value;
notifyListeners();
}

Future<void> submitForm() async {
_isLoading = true;
_error = null;
notifyListeners();

try {
// Xử lý form
await Future.delayed(Duration(seconds: 1)); // Giả lập API call
} catch (e) {
_error = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
}

3.2. Form với Provider

class ProviderForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => FormProvider(),
child: Consumer<FormProvider>(
builder: (context, formProvider, child) {
return Form(
child: Column(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
onChanged: formProvider.updateEmail,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
return null;
},
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
onChanged: formProvider.updatePassword,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
return null;
},
),
if (formProvider.error != null)
Text(
formProvider.error!,
style: TextStyle(color: Colors.red),
),
ElevatedButton(
onPressed: formProvider.isLoading
? null
: formProvider.submitForm,
child: formProvider.isLoading
? CircularProgressIndicator()
: Text('Submit'),
),
],
),
);
},
),
);
}
}

4. Best Practices

4.1. Tách biệt logic validation

class ValidationRules {
static String? validateEmail(String? value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
if (!emailRegex.hasMatch(value)) {
return 'Please enter a valid email';
}
return null;
}

static String? validatePassword(String? value) {
if (value == null || value.isEmpty) {
return 'Password is required';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
return null;
}
}

4.2. Xử lý lỗi và loading state

class FormState {
final bool isLoading;
final String? error;
final Map<String, String> fieldErrors;

FormState({
this.isLoading = false,
this.error,
this.fieldErrors = const {},
});

FormState copyWith({
bool? isLoading,
String? error,
Map<String, String>? fieldErrors,
}) {
return FormState(
isLoading: isLoading ?? this.isLoading,
error: error ?? this.error,
fieldErrors: fieldErrors ?? this.fieldErrors,
);
}
}

5. Ví dụ thực tế

5.1. Form đăng ký

class RegistrationForm extends StatefulWidget {
@override
_RegistrationFormState createState() => _RegistrationFormState();
}

class _RegistrationFormState extends State<RegistrationForm> {
final _formKey = GlobalKey<FormState>();
final _nameController = TextEditingController();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;

@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}

Future<void> _submitForm() async {
if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});

try {
// Xử lý đăng ký
await Future.delayed(Duration(seconds: 1));
// Chuyển hướng sau khi đăng ký thành công
} catch (e) {
// Hiển thị lỗi
} finally {
setState(() {
_isLoading = false;
});
}
}
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _nameController,
decoration: InputDecoration(
labelText: 'Full Name',
prefixIcon: Icon(Icons.person),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Name is required';
}
return null;
},
),
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: ValidationRules.validateEmail,
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: ValidationRules.validatePassword,
),
ElevatedButton(
onPressed: _isLoading ? null : _submitForm,
child: _isLoading
? CircularProgressIndicator()
: Text('Register'),
),
],
),
);
}
}

5.2. Form đăng nhập

class LoginForm extends StatefulWidget {
@override
_LoginFormState createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;
bool _obscurePassword = true;

@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}

Future<void> _submitForm() async {
if (_formKey.currentState!.validate()) {
setState(() {
_isLoading = true;
});

try {
// Xử lý đăng nhập
await Future.delayed(Duration(seconds: 1));
// Chuyển hướng sau khi đăng nhập thành công
} catch (e) {
// Hiển thị lỗi
} finally {
setState(() {
_isLoading = false;
});
}
}
}

@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: ValidationRules.validateEmail,
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility
: Icons.visibility_off,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
),
obscureText: _obscurePassword,
validator: ValidationRules.validatePassword,
),
ElevatedButton(
onPressed: _isLoading ? null : _submitForm,
child: _isLoading
? CircularProgressIndicator()
: Text('Login'),
),
],
),
);
}
}

Kết luận

Quản lý form và validation là một phần quan trọng trong phát triển ứng dụng Flutter. Việc hiểu và áp dụng đúng cách các kỹ thuật quản lý form và validation sẽ giúp bạn tạo ra trải nghiệm người dùng tốt hơn và giảm thiểu lỗi trong ứng dụng.


Tài liệu tham khảo: