Files
minhthu/lib/features/auth/presentation/widgets/login_form.dart
2025-10-28 23:46:07 +07:00

203 lines
6.0 KiB
Dart

import 'package:flutter/material.dart';
/// Reusable login form widget with validation
///
/// Handles username and password input with proper validation
class LoginForm extends StatefulWidget {
/// Callback when login button is pressed
final void Function(String username, String password) onSubmit;
/// Whether the form is in loading state
final bool isLoading;
const LoginForm({
super.key,
required this.onSubmit,
this.isLoading = false,
});
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
final _usernameController = TextEditingController(text: "yesterday305@gmail.com");
final _passwordController = TextEditingController(text: '123456');
bool _obscurePassword = true;
@override
void dispose() {
_usernameController.dispose();
_passwordController.dispose();
super.dispose();
}
void _handleSubmit() {
// Validate form
if (_formKey.currentState?.validate() ?? false) {
// Call submit callback
widget.onSubmit(
_usernameController.text.trim(),
_passwordController.text,
);
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Username field
TextFormField(
controller: _usernameController,
enabled: !widget.isLoading,
decoration: InputDecoration(
labelText: 'Tên đăng nhập',
hintText: 'Nhập tên đăng nhập',
prefixIcon: const Icon(Icons.person_outline),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
keyboardType: TextInputType.text,
textInputAction: TextInputAction.next,
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Vui lòng nhập tên đăng nhập';
}
if (value.trim().length < 3) {
return 'Tên đăng nhập phải có ít nhất 3 ký tự';
}
return null;
},
),
const SizedBox(height: 16),
// Password field
TextFormField(
controller: _passwordController,
enabled: !widget.isLoading,
obscureText: _obscurePassword,
decoration: InputDecoration(
labelText: 'Mật khẩu',
hintText: 'Nhập mật khẩu',
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_outlined
: Icons.visibility_off_outlined,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
),
textInputAction: TextInputAction.done,
onFieldSubmitted: (_) => _handleSubmit(),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Vui lòng nhập mật khẩu';
}
if (value.length < 6) {
return 'Mật khẩu phải có ít nhất 6 ký tự';
}
return null;
},
),
const SizedBox(height: 24),
// Login button
FilledButton.icon(
onPressed: widget.isLoading ? null : _handleSubmit,
icon: widget.isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Icon(Icons.login),
label: Text(widget.isLoading ? 'Đang đăng nhập...' : 'Đăng nhập'),
style: FilledButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
],
),
);
}
}
/// Simple text field widget for login forms
class LoginTextField extends StatelessWidget {
final TextEditingController controller;
final String label;
final String hint;
final IconData? prefixIcon;
final bool obscureText;
final TextInputType? keyboardType;
final TextInputAction? textInputAction;
final String? Function(String?)? validator;
final void Function(String)? onFieldSubmitted;
final bool enabled;
final Widget? suffixIcon;
const LoginTextField({
super.key,
required this.controller,
required this.label,
required this.hint,
this.prefixIcon,
this.obscureText = false,
this.keyboardType,
this.textInputAction,
this.validator,
this.onFieldSubmitted,
this.enabled = true,
this.suffixIcon,
});
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
enabled: enabled,
obscureText: obscureText,
keyboardType: keyboardType,
textInputAction: textInputAction,
onFieldSubmitted: onFieldSubmitted,
decoration: InputDecoration(
labelText: label,
hintText: hint,
prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null,
suffixIcon: suffixIcon,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
filled: true,
fillColor: enabled
? Theme.of(context).colorScheme.surface
: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3),
),
validator: validator,
);
}
}