/// Reusable validated text field with consistent styling library validated_text_field; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; /// Validated text field with consistent styling and behavior class ValidatedTextField extends StatelessWidget { final TextEditingController? controller; final String? labelText; final String? hintText; final String? helperText; final String? initialValue; final String? Function(String?)? validator; final void Function(String)? onChanged; final void Function(String?)? onSaved; final TextInputType? keyboardType; final TextInputAction? textInputAction; final bool obscureText; final bool enabled; final bool readOnly; final int? maxLines; final int? minLines; final int? maxLength; final Widget? prefixIcon; final Widget? suffixIcon; final List? inputFormatters; final FocusNode? focusNode; final void Function()? onEditingComplete; final void Function(String)? onFieldSubmitted; final AutovalidateMode? autovalidateMode; final bool showCounter; const ValidatedTextField({ super.key, this.controller, this.labelText, this.hintText, this.helperText, this.initialValue, this.validator, this.onChanged, this.onSaved, this.keyboardType, this.textInputAction, this.obscureText = false, this.enabled = true, this.readOnly = false, this.maxLines = 1, this.minLines, this.maxLength, this.prefixIcon, this.suffixIcon, this.inputFormatters, this.focusNode, this.onEditingComplete, this.onFieldSubmitted, this.autovalidateMode, this.showCounter = true, }); @override Widget build(BuildContext context) { return TextFormField( controller: controller, initialValue: initialValue, decoration: InputDecoration( labelText: labelText, hintText: hintText, helperText: helperText, prefixIcon: prefixIcon, suffixIcon: suffixIcon, border: const OutlineInputBorder(), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey.shade400, width: 1.0, ), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.blue, width: 2.0, ), ), errorBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.red, width: 1.0, ), ), focusedErrorBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.red, width: 2.0, ), ), filled: !enabled, fillColor: !enabled ? Colors.grey.shade100 : null, counterText: showCounter ? null : '', ), validator: validator, onChanged: onChanged, onSaved: onSaved, keyboardType: keyboardType, textInputAction: textInputAction, obscureText: obscureText, enabled: enabled, readOnly: readOnly, maxLines: maxLines, minLines: minLines, maxLength: maxLength, inputFormatters: inputFormatters, focusNode: focusNode, onEditingComplete: onEditingComplete, onFieldSubmitted: onFieldSubmitted, autovalidateMode: autovalidateMode, ); } } /// Validated amount field with currency formatting class ValidatedAmountField extends StatelessWidget { final TextEditingController? controller; final String? labelText; final String? hintText; final String? initialValue; final String? Function(String?)? validator; final void Function(String)? onChanged; final void Function(String?)? onSaved; final bool enabled; final String currencySymbol; final FocusNode? focusNode; const ValidatedAmountField({ super.key, this.controller, this.labelText, this.hintText, this.initialValue, this.validator, this.onChanged, this.onSaved, this.enabled = true, this.currencySymbol = 'FCFA', this.focusNode, }); @override Widget build(BuildContext context) { return ValidatedTextField( controller: controller, initialValue: initialValue, labelText: labelText, hintText: hintText, helperText: 'Entrez un montant positif (max 2 décimales)', validator: validator, onChanged: onChanged, onSaved: onSaved, enabled: enabled, focusNode: focusNode, keyboardType: const TextInputType.numberWithOptions(decimal: true), textInputAction: TextInputAction.next, suffixIcon: Padding( padding: const EdgeInsets.all(12.0), child: Text( currencySymbol, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.grey, ), ), ), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,2}')), ], ); } } /// Validated dropdown field class ValidatedDropdownField extends StatelessWidget { final T? value; final List> items; final String? labelText; final String? hintText; final String? helperText; final String? Function(T?)? validator; final void Function(T?)? onChanged; final void Function(T?)? onSaved; final bool enabled; const ValidatedDropdownField({ super.key, this.value, required this.items, this.labelText, this.hintText, this.helperText, this.validator, this.onChanged, this.onSaved, this.enabled = true, }); @override Widget build(BuildContext context) { return DropdownButtonFormField( value: value, items: items, decoration: InputDecoration( labelText: labelText, hintText: hintText, helperText: helperText, border: const OutlineInputBorder(), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey.shade400, width: 1.0, ), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.blue, width: 2.0, ), ), errorBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.red, width: 1.0, ), ), filled: !enabled, fillColor: !enabled ? Colors.grey.shade100 : null, ), validator: validator, onChanged: enabled ? onChanged : null, onSaved: onSaved, ); } } /// Validated date picker field class ValidatedDateField extends StatelessWidget { final DateTime? selectedDate; final String? labelText; final String? hintText; final String? helperText; final String? Function(DateTime?)? validator; final void Function(DateTime)? onChanged; final DateTime? firstDate; final DateTime? lastDate; final bool enabled; const ValidatedDateField({ super.key, this.selectedDate, this.labelText, this.hintText, this.helperText, this.validator, this.onChanged, this.firstDate, this.lastDate, this.enabled = true, }); @override Widget build(BuildContext context) { return InkWell( onTap: enabled ? () async { final date = await showDatePicker( context: context, initialDate: selectedDate ?? DateTime.now(), firstDate: firstDate ?? DateTime(2000), lastDate: lastDate ?? DateTime(2100), ); if (date != null && onChanged != null) { onChanged!(date); } } : null, child: InputDecorator( decoration: InputDecoration( labelText: labelText, hintText: hintText, helperText: helperText, suffixIcon: const Icon(Icons.calendar_today), border: const OutlineInputBorder(), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.grey.shade400, width: 1.0, ), ), focusedBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.blue, width: 2.0, ), ), errorBorder: const OutlineInputBorder( borderSide: BorderSide( color: Colors.red, width: 1.0, ), ), filled: !enabled, fillColor: !enabled ? Colors.grey.shade100 : null, errorText: validator != null ? validator!(selectedDate) : null, ), child: Text( selectedDate != null ? '${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}' : hintText ?? 'Sélectionner une date', style: TextStyle( color: selectedDate != null ? Colors.black87 : Colors.grey, ), ), ), ); } }