import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'admin_detail_modal.dart'; /// Bloc type formulaire (titre de section + champs read-only) pour les modales de validation. /// [rowLayout] : même disposition que la création de compte, ex. [2, 2, 1, 2] = ligne de 2, ligne de 2, plein largeur, ligne de 2. /// [rowFlex] : flex par index de ligne (optionnel). Ex. {3: [2, 5]} = 4e ligne : code postal étroit (2), ville large (5). class ValidationDetailSection extends StatelessWidget { /// Si null ou vide, pas de bandeau titre (gain de place vertical, ex. wizard AM). final String? title; final List fields; /// Nombre de champs par ligne (1 = plein largeur, 2 = deux côte à côte). Ex. [2, 2, 1, 2] pour identité. final List? rowLayout; /// Flex par ligne (index de ligne -> [flex1, flex2, ...]). Ex. {3: [2, 5]} pour Code postal | Ville. final Map>? rowFlex; const ValidationDetailSection({ super.key, this.title, required this.fields, this.rowLayout, this.rowFlex, }); @override Widget build(BuildContext context) { return ValidationFormGrid( title: title, rowLayout: rowLayout, rowFlex: rowFlex, fields: fields .map( (f) => ValidationLabeledField( label: f.label, field: ValidationReadOnlyField(value: f.value), ), ) .toList(), ); } } /// Grille label/champ réutilisable (validation, fiches admin). class ValidationFormGrid extends StatelessWidget { final String? title; final List fields; final List? rowLayout; final Map>? rowFlex; final bool compact; const ValidationFormGrid({ super.key, this.title, required this.fields, this.rowLayout, this.rowFlex, this.compact = false, }); @override Widget build(BuildContext context) { final layout = rowLayout ?? List.filled(fields.length, 1); int index = 0; int rowIndex = 0; final rows = []; for (final count in layout) { if (index >= fields.length) break; final rowFields = fields.skip(index).take(count).toList(); index += count; if (rowFields.isEmpty) continue; final flexForRow = rowFlex?[rowIndex]; rowIndex++; if (count == 1) { rows.add(Padding( padding: EdgeInsets.only(bottom: compact ? 8 : 12), child: rowFields.first, )); } else { rows.add(Padding( padding: EdgeInsets.only(bottom: compact ? 8 : 12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (int i = 0; i < rowFields.length; i++) ...[ if (i > 0) SizedBox(width: compact ? 12 : 16), Expanded( flex: (flexForRow != null && i < flexForRow.length) ? flexForRow[i] : 1, child: rowFields[i], ), ], ], ), )); } } final showTitle = title != null && title!.trim().isNotEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, mainAxisSize: MainAxisSize.min, children: [ if (showTitle) ...[ Text( title!.trim(), style: TextStyle( fontSize: compact ? 15 : 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: compact ? 8 : 12), ], ...rows, ], ); } } /// Décoration commune lecture seule / édition (modales validation, fiches admin). class ValidationFieldDecoration { ValidationFieldDecoration._(); static InputDecoration input({String? hint, bool compact = false}) { return InputDecoration( isDense: true, filled: true, fillColor: Colors.grey.shade50, hintText: hint, contentPadding: EdgeInsets.symmetric( horizontal: compact ? 10 : 12, vertical: compact ? 7 : 10, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: Colors.grey.shade300), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(6), borderSide: BorderSide(color: Colors.grey.shade500), ), ); } static BoxDecoration container() { return BoxDecoration( color: Colors.grey.shade50, borderRadius: BorderRadius.circular(6), border: Border.all(color: Colors.grey.shade300), ); } } /// Libellé au-dessus d’un champ (même typo que [ValidationDetailSection]). class ValidationLabeledField extends StatelessWidget { final String label; final Widget field; const ValidationLabeledField({ super.key, required this.label, required this.field, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( label, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), ), const SizedBox(height: 4), field, ], ); } } /// Champ texte éditable, même rendu que [ValidationReadOnlyField]. class ValidationEditableField extends StatelessWidget { final TextEditingController controller; final TextInputType keyboardType; final List? inputFormatters; final String? hintText; final int maxLines; final bool compact; const ValidationEditableField({ super.key, required this.controller, this.keyboardType = TextInputType.text, this.inputFormatters, this.hintText, this.maxLines = 1, this.compact = false, }); static const double _compactFieldHeight = 34; @override Widget build(BuildContext context) { if (!compact || maxLines > 1) { return TextField( controller: controller, keyboardType: keyboardType, inputFormatters: inputFormatters, maxLines: maxLines, style: TextStyle( color: Colors.black87, fontSize: compact ? 13 : 14, ), decoration: ValidationFieldDecoration.input( hint: hintText, compact: compact, ), ); } return SizedBox( height: _compactFieldHeight, child: TextField( controller: controller, keyboardType: keyboardType, inputFormatters: inputFormatters, maxLines: 1, style: const TextStyle(color: Colors.black87, fontSize: 13, height: 1.15), decoration: ValidationFieldDecoration.input( hint: hintText, compact: true, ).copyWith( contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 9), ), ), ); } } /// Grille label/champ éditable (délègue à [ValidationFormGrid]). class ValidationEditableSection extends StatelessWidget { final List fields; final List? rowLayout; final Map>? rowFlex; final bool compact; const ValidationEditableSection({ super.key, required this.fields, this.rowLayout, this.rowFlex, this.compact = false, }); @override Widget build(BuildContext context) { return ValidationFormGrid( rowLayout: rowLayout, rowFlex: rowFlex, compact: compact, fields: fields, ); } } /// Champ texte en lecture seule, style formulaire (fond gris léger, bordure). class ValidationReadOnlyField extends StatelessWidget { final String value; final int? maxLines; final bool compact; const ValidationReadOnlyField({ super.key, required this.value, this.maxLines = 1, this.compact = false, }); static const double _compactFieldHeight = 34; @override Widget build(BuildContext context) { return Container( width: double.infinity, height: compact && maxLines == 1 ? _compactFieldHeight : null, alignment: compact ? Alignment.centerLeft : null, padding: EdgeInsets.symmetric( horizontal: compact ? 10 : 12, vertical: compact ? 7 : 10, ), decoration: ValidationFieldDecoration.container(), child: Text( value, style: TextStyle( color: Colors.black87, fontSize: compact ? 13 : 14, height: compact ? 1.2 : null, ), maxLines: maxLines, overflow: TextOverflow.ellipsis, ), ); } }