import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class CustomTextField extends StatelessWidget { final TextEditingController? controller; final String label; final String? hintText; final String? helperText; final int? helperMaxLines; final IconData? prefixIcon; final Widget? suffixIcon; final bool readOnly; final bool isRequired; final bool autofocus; final FocusNode? focusNode; final String? Function(String?)? validator; final VoidCallback? onTap; final TextInputType? keyboardType; final List? inputFormatters; final int? maxLines; final int? maxLength; final bool obscureText; final Function(String)? onChanged; final Function(String)? onFieldSubmitted; // Nouvelles propriétés pour le formulaire de passage final TextAlign? textAlign; final bool showLabel; final EdgeInsets? contentPadding; const CustomTextField({ super.key, this.controller, required this.label, this.hintText, this.helperText, this.helperMaxLines, this.prefixIcon, this.suffixIcon, this.readOnly = false, this.isRequired = false, this.autofocus = false, this.focusNode, this.validator, this.onTap, this.keyboardType, this.inputFormatters, this.maxLines = 1, this.maxLength, this.obscureText = false, this.onChanged, this.onFieldSubmitted, // Nouvelles propriétés pour le formulaire de passage this.textAlign, this.showLabel = true, this.contentPadding, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); // Mode sans label externe (pour utilisation dans des sections avec titres flottants) if (!showLabel) { return TextFormField( controller: controller, focusNode: focusNode, readOnly: readOnly, autofocus: autofocus, onTap: onTap, validator: validator, keyboardType: keyboardType, inputFormatters: inputFormatters, maxLines: maxLines, maxLength: maxLength, obscureText: obscureText, onChanged: onChanged, onFieldSubmitted: onFieldSubmitted, textAlign: textAlign ?? TextAlign.start, decoration: InputDecoration( labelText: isRequired ? "$label *" : label, hintText: hintText, helperText: helperText, helperMaxLines: helperMaxLines, prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null, suffixIcon: suffixIcon, border: const OutlineInputBorder(), floatingLabelBehavior: FloatingLabelBehavior.always, contentPadding: contentPadding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 12), ), buildCounter: maxLength != null ? (context, {required currentLength, required isFocused, maxLength}) { return Padding( padding: const EdgeInsets.only(top: 4), child: Text( '$currentLength/${maxLength ?? 0}', style: theme.textTheme.bodySmall?.copyWith( color: currentLength > (maxLength ?? 0) * 0.8 ? theme.colorScheme.error : theme.colorScheme.onSurface.withValues(alpha: 0.6), ), ), ); } : null, ); } // Mode standard avec label externe return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Label avec indicateur de champ requis if (label.isNotEmpty) ...[ Row( children: [ Text( label, style: theme.textTheme.bodyMedium?.copyWith( fontWeight: FontWeight.w500, color: theme.colorScheme.onSurface, ), ), if (isRequired) ...[ const SizedBox(width: 4), Text( '*', style: TextStyle( color: theme.colorScheme.error, fontWeight: FontWeight.bold, ), ), ], ], ), const SizedBox(height: 8), ], // Champ de texte TextFormField( controller: controller, focusNode: focusNode, readOnly: readOnly, autofocus: autofocus, onTap: onTap, validator: validator, keyboardType: keyboardType, inputFormatters: inputFormatters, maxLines: maxLines, maxLength: maxLength, obscureText: obscureText, onChanged: onChanged, onFieldSubmitted: onFieldSubmitted, textAlign: textAlign ?? TextAlign.start, decoration: InputDecoration( hintText: hintText, helperText: helperText, helperMaxLines: helperMaxLines, prefixIcon: prefixIcon != null ? Icon(prefixIcon) : null, suffixIcon: suffixIcon, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: theme.colorScheme.outline, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: theme.colorScheme.outline.withValues(alpha: 0.5), ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: theme.colorScheme.primary, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: theme.colorScheme.error, width: 2, ), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: theme.colorScheme.error, width: 2, ), ), filled: true, fillColor: readOnly ? theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.3) : theme.colorScheme.surface, contentPadding: contentPadding ?? const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), buildCounter: maxLength != null ? (context, {required currentLength, required isFocused, maxLength}) { return Padding( padding: const EdgeInsets.only(top: 4), child: Text( '$currentLength/${maxLength ?? 0}', style: theme.textTheme.bodySmall?.copyWith( color: currentLength > (maxLength ?? 0) * 0.8 ? theme.colorScheme.error : theme.colorScheme.onSurface.withValues(alpha: 0.6), ), ), ); } : null, ), ], ); } }