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 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; const CustomTextField({ super.key, this.controller, required this.label, this.hintText, this.helperText, 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, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); 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, decoration: InputDecoration( hintText: hintText, helperText: helperText, 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.withOpacity(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.withOpacity(0.3) : theme.colorScheme.surface, 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.withOpacity(0.6), ), ), ); } : null, ), ], ); } }