import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import '../Provider/language_provider.dart'; import '../utils/colors.dart'; import 'custom_text.dart'; class CustomTextFieldWidget extends StatefulWidget { final TextEditingController controller; final String label; final IconData? icon; final bool isPassword; final TextInputType keybordType; final String? Function(String?)? validator; final bool? isRequired; // New optional parameter final String? hintText; const CustomTextFieldWidget({ Key? key, required this.controller, required this.label, this.icon, this.hintText, required this.isPassword, required this.keybordType, this.validator, this.isRequired, // Optional parameter - null means no indicator shown }) : super(key: key); @override State createState() => _CustomTextFieldWidgetState(); } class _CustomTextFieldWidgetState extends State with SingleTickerProviderStateMixin { bool _isFocused = false; bool _isPasswordVisible = false; bool _hasContent = false; late AnimationController _animationController; late Animation _scaleAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 200), vsync: this, ); _scaleAnimation = Tween(begin: 1.0, end: 1.02).animate( CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), ); widget.controller.addListener(() { if (!mounted) return; setState(() { _hasContent = widget.controller.text.isNotEmpty; }); }); } @override void dispose() { _animationController.dispose(); super.dispose(); } // Helper method to get the label with required/optional indicator String get _labelWithIndicator { if (widget.isRequired == null) { return widget.label; // No indicator if isRequired is not specified } return widget.isRequired! ? '${widget.label} *' // Add asterisk for required fields : '${widget.label} (Optional)'; // Add "(Optional)" for optional fields } // Helper widget for individual requirement items Widget _buildRequirementItem(String text, bool isValid, bool isDarkMode) { return Row( children: [ Icon( isValid ? Icons.check_circle : Icons.radio_button_unchecked, size: 16, color: isValid ? Colors.green.shade600 : isDarkMode ? Colors.grey.shade500 : Colors.grey.shade400, ), const SizedBox(width: 8), Expanded( child: Txt( text: text, style: TextStyle( fontSize: 11, color: isValid ? Colors.green.shade600 : isDarkMode ? Colors.grey.shade400 : Colors.grey.shade600, fontFamily: 'Poppins', fontWeight: isValid ? FontWeight.w500 : FontWeight.w400, ), ), ), ], ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDarkMode = theme.brightness == Brightness.dark; // Use theme colors for text and primary elements final Color activeColor = theme.primaryColor; final Color defaultTextColor = theme.textTheme.bodyLarge?.color ?? (isDarkMode ? AppColors.white : AppColors.black); final Color defaultIconLabelColor = theme.inputDecorationTheme.prefixIconColor ?? (isDarkMode ? AppColors.darkSecondaryText : const Color(0xFFA2A2A2)); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 5.0), child: AnimatedBuilder( animation: _scaleAnimation, builder: (context, child) { return Transform.scale( scale: _scaleAnimation.value, child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(30), boxShadow: _isFocused ? [ BoxShadow( color: activeColor.withOpacity(0.2), blurRadius: 8, offset: const Offset(0, 4), ), ] : [ BoxShadow( color: isDarkMode ? Colors.black.withOpacity(0.2) : Colors.black.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: TextField( controller: widget.controller, obscureText: widget.isPassword && !_isPasswordVisible, keyboardType: widget.keybordType, inputFormatters: widget.label == 'Id card number' ? [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(8), ] : widget.label == '123' ? [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(3), ] : widget.label == '1234' || widget.label == 'Code' ? [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(4), ] : widget.label == '12' ? [ FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(2), ] : [], onTap: () { setState(() { _isFocused = true; }); _animationController.forward(); }, onChanged: (value) { setState(() { _hasContent = value.isNotEmpty; }); }, onEditingComplete: () { setState(() { _isFocused = false; }); _animationController.reverse(); }, style: TextStyle( fontFamily: 'Poppins', fontSize: 20, color: defaultTextColor, fontWeight: FontWeight.w500, ), decoration: InputDecoration( filled: true, hintText: (!_isFocused && !_hasContent) ? widget.hintText : null, hintStyle: TextStyle( fontFamily: 'Poppins', fontSize: 16, color: isDarkMode ? AppColors.darkSecondaryText.withOpacity(0.5) : Colors.grey.shade400, fontWeight: FontWeight.w400, ), fillColor: _isFocused ? isDarkMode ? AppColors.blackBackgroundColor : AppColors.darkSecondaryText2 : isDarkMode ? AppColors.blackBackgroundColor : AppColors.darkSecondaryText2, prefixIcon: widget.icon!=null ?Icon( widget.icon, color: _isFocused || _hasContent ? activeColor // Active state icon color : defaultIconLabelColor, size: 18, ): null, suffixIcon: widget.isPassword ? IconButton( icon: AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: Icon( _isPasswordVisible ? Icons.visibility : Icons.visibility_off, key: ValueKey(_isPasswordVisible), color: _isFocused ? activeColor : defaultIconLabelColor, size: 18, ), ), onPressed: () { setState(() { _isPasswordVisible = !_isPasswordVisible; }); }, ) : null, label: RichText( text: TextSpan( children: [ TextSpan( text: languageSingleton.translate(widget.label), style: TextStyle( color: _isFocused || _hasContent ? activeColor : defaultIconLabelColor, fontSize: _isFocused || _hasContent ? 18 : 20, fontFamily: 'Poppins', fontWeight: _isFocused || _hasContent ? FontWeight.w600 : FontWeight.w400, ), ), // Add required/optional indicator if (widget.isRequired != null) ...[ if (widget.isRequired!) TextSpan( text: ' *', style: TextStyle( color: Colors.red, fontSize: _isFocused || _hasContent ? 11 : 14, fontFamily: 'Poppins', fontWeight: FontWeight.w600, ), ) else TextSpan( text: languageSingleton.translate(' (Optional)'), style: TextStyle( color: isDarkMode ? AppColors.darkSecondaryText.withOpacity(0.7) : const Color(0xFF999999), fontSize: _isFocused || _hasContent ? 10 : 12, fontFamily: 'Poppins', fontWeight: FontWeight.w400, fontStyle: FontStyle.italic, ), ), ], ], ), ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(15), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey.shade200, width: 1, ), borderRadius: BorderRadius.circular(15), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: activeColor, width: 1.5, ), borderRadius: BorderRadius.circular(15), ), errorBorder: OutlineInputBorder( borderSide: const BorderSide( color: Colors.red, width: 1.5, ), borderRadius: BorderRadius.circular(15), ), focusedErrorBorder: OutlineInputBorder( borderSide: const BorderSide( color: Colors.red, width: 1.5, ), borderRadius: BorderRadius.circular(15), ), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 16, ), ), ), ), ); }, ), ), ], ); } }