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 createState() => _LoginFormState(); } class _LoginFormState extends State { final _formKey = GlobalKey(); 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, ); } }