From 8ee2ca8ea6bd6e4a3e53076c5b23288bb70154e8 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Thu, 23 Jul 2026 16:49:59 +0200 Subject: [PATCH] fix(#156): caler la modale AM sur les TF et unifier create/review. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ValidationFormMetrics (titres, TF, écarts) pilote la hauteur ; photo étirée sur le corps ; mêmes widgets create et validation. Co-authored-by: Cursor --- .../admin/am_dossier_create_modal.dart | 3 +- .../lib/widgets/admin/am_dossier_wizard.dart | 86 ++--- .../common/validation_detail_section.dart | 323 ++++++++++++------ .../admin/validation_dossier_modal.dart | 10 +- .../lib/widgets/common/identity_block.dart | 12 +- 5 files changed, 272 insertions(+), 162 deletions(-) diff --git a/frontend/lib/widgets/admin/am_dossier_create_modal.dart b/frontend/lib/widgets/admin/am_dossier_create_modal.dart index 412ec82..f221d93 100644 --- a/frontend/lib/widgets/admin/am_dossier_create_modal.dart +++ b/frontend/lib/widgets/admin/am_dossier_create_modal.dart @@ -33,7 +33,8 @@ class _AmDossierCreateModalState extends State { } static const double _modalWidth = 930; - static const double _bodyHeight = 435; + /// Hauteur calculée depuis 4 lignes de TF (voir [AmDossierWizard.shellBodyHeight]). + static double get _bodyHeight => AmDossierWizard.shellBodyHeight; @override Widget build(BuildContext context) { diff --git a/frontend/lib/widgets/admin/am_dossier_wizard.dart b/frontend/lib/widgets/admin/am_dossier_wizard.dart index dca588f..affb5ea 100644 --- a/frontend/lib/widgets/admin/am_dossier_wizard.dart +++ b/frontend/lib/widgets/admin/am_dossier_wizard.dart @@ -76,6 +76,10 @@ class AmDossierWizard extends StatefulWidget { bool get isCreate => mode == AmDossierWizardMode.create; + /// Hauteur corps modale AM — dérivée de [ValidationFormMetrics] (4 lignes). + static double get shellBodyHeight => + ValidationFormMetrics.shellBodyHeightForRows(4); + @override State createState() => _AmDossierWizardState(); } @@ -584,56 +588,45 @@ class _AmDossierWizardState extends State { Widget _buildStep0() { if (_isCreate) { - return LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: IdentityBlock.editable( - title: 'Identité et coordonnées', - nomController: _nomCtrl, - prenomController: _prenomCtrl, - telephoneController: _telCtrl, - emailController: _emailCtrl, - adresseController: _adresseCtrl, - codePostalController: _cpCtrl, - villeController: _villeCtrl, - ), - ), - ); - }, + return IdentityBlock.editable( + title: 'Identité et coordonnées', + nomController: _nomCtrl, + prenomController: _prenomCtrl, + telephoneController: _telCtrl, + emailController: _emailCtrl, + adresseController: _adresseCtrl, + codePostalController: _cpCtrl, + villeController: _villeCtrl, ); } final u = _dossier.user; - return LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: IdentityBlock.readOnlyFromUser( - u, - title: 'Identité et coordonnées', - emptyLabel: '–', - ), - ), - ); - }, + return IdentityBlock.readOnlyFromUser( + u, + title: 'Identité et coordonnées', + emptyLabel: '–', ); } Widget _buildStep1() { + // Modale calée sur les TF ; photo étirée sur toute la hauteur utile + // (largeur = [AdminAmPhotoFrame.columnWidthForHeight], cadre inclus). return LayoutBuilder( builder: (context, c) { final maxRowW = c.maxWidth; - final maxRowH = c.maxHeight; - const photoHeaderH = 0.0; - final bodyH = (maxRowH - photoHeaderH).clamp(0.0, double.infinity); - final idealPhotoW = bodyH * _idPhotoAspectRatio + 16; + final maxRowH = c.maxHeight.clamp(0.0, double.infinity); final maxPhotoW = (maxRowW - _photoProGap - _proColumnMinWidth) .clamp(0.0, double.infinity); - var photoW = idealPhotoW.clamp(_photoColumnMinWidth, 360.0); + var photoW = AdminAmPhotoFrame.columnWidthForHeight(maxRowH) + .clamp(_photoColumnMinWidth, 360.0); if (photoW > maxPhotoW) photoW = maxPhotoW; - photoW = photoW.clamp(0.0, maxRowW - _photoProGap); + + final form = _isCreate + ? _buildCreateProFields() + : ValidationDetailSection( + title: 'Dossier professionnel', + fields: _photoProFields(_dossier), + rowLayout: _photoProRowLayout, + ); return Row( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -651,22 +644,9 @@ class _AmDossierWizardState extends State { ), const SizedBox(width: _photoProGap), Expanded( - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: - BoxConstraints(minWidth: constraints.maxWidth), - child: _isCreate - ? _buildCreateProFields() - : ValidationDetailSection( - title: 'Dossier professionnel', - fields: _photoProFields(_dossier), - rowLayout: _photoProRowLayout, - ), - ), - ); - }, + child: Align( + alignment: Alignment.topCenter, + child: form, ), ), ], diff --git a/frontend/lib/widgets/admin/common/validation_detail_section.dart b/frontend/lib/widgets/admin/common/validation_detail_section.dart index b133748..faf9f6e 100644 --- a/frontend/lib/widgets/admin/common/validation_detail_section.dart +++ b/frontend/lib/widgets/admin/common/validation_detail_section.dart @@ -6,6 +6,50 @@ import 'package:p_tits_pas/utils/phone_utils.dart'; import 'package:p_tits_pas/utils/postal_utils.dart'; import 'admin_detail_modal.dart'; +/// Réglages des formulaires validation / wizard AM — **jouer sur ces 3 leviers**. +class ValidationFormMetrics { + ValidationFormMetrics._(); + + // --- 1. Titres de section --- + static const double sectionTitleFontSize = 16; + static const double sectionTitleGapBelow = 12; + + // --- 2. TF : texte intérieur + padding vertical (= hauteur) --- + static const double fieldTextFontSize = 14; + static const double fieldContentPaddingV = 12; + static const double fieldContentPaddingH = 12; + /// Hauteur estimée du TF (texte + padding haut/bas + bordure). + static const double fieldHeight = + fieldTextFontSize + fieldContentPaddingV * 2 + 4; + + // --- 3. Espace entre les lignes de TF --- + static const double rowGapBelow = 12; + + // Libellé au-dessus du TF (titre du champ) + static const double fieldLabelFontSize = 13; + static const double fieldLabelGapBelow = 4; + + static const TextStyle fieldTextStyle = TextStyle( + color: Colors.black87, + fontSize: fieldTextFontSize, + ); + + static double get sectionTitleBlockHeight => + sectionTitleFontSize + sectionTitleGapBelow; + + static double get labeledRowHeight => + fieldLabelFontSize + fieldLabelGapBelow + fieldHeight + rowGapBelow; + + /// Corps modale AM : padding wizard + titre + [rows] lignes + nav. + static double shellBodyHeightForRows(int rows) => + 20 * 2 + // padding wizard + 4 + // espace haut + sectionTitleBlockHeight + + rows * labeledRowHeight + + 24 + // avant nav + 44; // boutons +} + /// 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). @@ -20,12 +64,16 @@ class ValidationDetailSection extends StatelessWidget { /// Flex par ligne (index de ligne -> [flex1, flex2, ...]). Ex. {3: [2, 5]} pour Code postal | Ville. final Map>? rowFlex; + /// Remplit la hauteur disponible (wizard AM étapes 1–2). + final bool expandVertically; + const ValidationDetailSection({ super.key, this.title, required this.fields, this.rowLayout, this.rowFlex, + this.expandVertically = false, }); @override @@ -34,6 +82,7 @@ class ValidationDetailSection extends StatelessWidget { title: title, rowLayout: rowLayout, rowFlex: rowFlex, + expandVertically: expandVertically, fields: fields .map( (f) => ValidationLabeledField( @@ -53,6 +102,8 @@ class ValidationFormGrid extends StatelessWidget { final List? rowLayout; final Map>? rowFlex; final bool compact; + /// Répartit la hauteur dispo entre les lignes (remplit le blanc sans scroll). + final bool expandVertically; const ValidationFormGrid({ super.key, @@ -61,6 +112,7 @@ class ValidationFormGrid extends StatelessWidget { this.rowLayout, this.rowFlex, this.compact = false, + this.expandVertically = false, }); @override @@ -68,7 +120,7 @@ class ValidationFormGrid extends StatelessWidget { final layout = rowLayout ?? List.filled(fields.length, 1); int index = 0; int rowIndex = 0; - final rows = []; + final rowWidgets = []; for (final count in layout) { if (index >= fields.length) break; final rowFields = fields.skip(index).take(count).toList(); @@ -76,48 +128,73 @@ class ValidationFormGrid extends StatelessWidget { if (rowFields.isEmpty) continue; final flexForRow = rowFlex?[rowIndex]; rowIndex++; + final labeled = rowFields + .map( + (f) => ValidationLabeledField( + label: f.label, + field: f.field, + expand: expandVertically, + ), + ) + .toList(); + + Widget row; if (count == 1) { - rows.add(Padding( - padding: EdgeInsets.only(bottom: compact ? 8 : 12), - child: rowFields.first, - )); + row = labeled.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], - ), - ], + row = Row( + crossAxisAlignment: expandVertically + ? CrossAxisAlignment.stretch + : CrossAxisAlignment.start, + children: [ + for (int i = 0; i < labeled.length; i++) ...[ + if (i > 0) SizedBox(width: compact ? 12 : 16), + Expanded( + flex: (flexForRow != null && i < flexForRow.length) + ? flexForRow[i] + : 1, + child: labeled[i], + ), ], + ], + ); + } + + if (expandVertically) { + rowWidgets.add(Expanded(child: row)); + } else { + rowWidgets.add(Padding( + padding: EdgeInsets.only( + bottom: compact ? 8 : ValidationFormMetrics.rowGapBelow, ), + child: row, )); } } + final showTitle = title != null && title!.trim().isNotEmpty; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, + mainAxisSize: expandVertically ? MainAxisSize.max : MainAxisSize.min, children: [ if (showTitle) ...[ Text( title!.trim(), style: TextStyle( - fontSize: compact ? 15 : 16, + fontSize: compact + ? ValidationFormMetrics.sectionTitleFontSize - 1 + : ValidationFormMetrics.sectionTitleFontSize, fontWeight: FontWeight.w600, color: Colors.black87, ), ), - SizedBox(height: compact ? 8 : 12), + SizedBox( + height: compact + ? 8 + : ValidationFormMetrics.sectionTitleGapBelow, + ), ], - ...rows, + ...rowWidgets, ], ); } @@ -134,8 +211,8 @@ class ValidationFieldDecoration { fillColor: Colors.grey.shade50, hintText: hint, contentPadding: EdgeInsets.symmetric( - horizontal: compact ? 10 : 12, - vertical: compact ? 7 : 10, + horizontal: compact ? 10 : ValidationFormMetrics.fieldContentPaddingH, + vertical: compact ? 7 : ValidationFormMetrics.fieldContentPaddingV, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(6), @@ -184,29 +261,31 @@ class ValidationFieldDecoration { class ValidationLabeledField extends StatelessWidget { final String label; final Widget field; + final bool expand; const ValidationLabeledField({ super.key, required this.label, required this.field, + this.expand = false, }); @override Widget build(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, + mainAxisSize: expand ? MainAxisSize.max : MainAxisSize.min, children: [ Text( label, style: TextStyle( - fontSize: 12, + fontSize: ValidationFormMetrics.fieldLabelFontSize, fontWeight: FontWeight.w500, color: Colors.grey.shade700, ), ), - const SizedBox(height: 4), - field, + SizedBox(height: ValidationFormMetrics.fieldLabelGapBelow), + if (expand) Expanded(child: field) else field, ], ); } @@ -268,13 +347,16 @@ class ValidationEditableField extends StatelessWidget { ); } if (!compact) { - return TextField( - controller: controller, - keyboardType: keyboardType, - inputFormatters: inputFormatters, - maxLines: 1, - style: const TextStyle(color: Colors.black87, fontSize: 14), - decoration: ValidationFieldDecoration.input(hint: hintText), + return _validationFieldFillHeight( + TextField( + controller: controller, + keyboardType: keyboardType, + inputFormatters: inputFormatters, + maxLines: 1, + textAlignVertical: TextAlignVertical.center, + style: ValidationFormMetrics.fieldTextStyle, + decoration: ValidationFieldDecoration.input(hint: hintText), + ), ); } return SizedBox( @@ -299,6 +381,20 @@ class ValidationEditableField extends StatelessWidget { } } +/// Étire le champ si le parent impose une hauteur (grille [expandVertically]). +Widget _validationFieldFillHeight(Widget field) { + return LayoutBuilder( + builder: (context, c) { + if (!c.hasBoundedHeight || !c.maxHeight.isFinite) return field; + return SizedBox( + height: c.maxHeight, + width: double.infinity, + child: field, + ); + }, + ); +} + /// E-mail style validation — même supervision que login / création de compte : /// [EmailMaxLengthFormatter] + à la perte de focus : trim/minuscules + validation. class ValidationEmailField extends StatefulWidget { @@ -354,23 +450,27 @@ class _ValidationEmailFieldState extends State { @override Widget build(BuildContext context) { - return TextFormField( - key: _fieldKey, - controller: widget.controller, - focusNode: _focusNode, - keyboardType: TextInputType.emailAddress, - autocorrect: false, - enableSuggestions: false, - autofillHints: const [AutofillHints.email], - textInputAction: TextInputAction.next, - inputFormatters: const [EmailMaxLengthFormatter()], - style: const TextStyle(color: Colors.black87, fontSize: 14), - decoration: ValidationFieldDecoration.input(hint: widget.hintText).copyWith( - errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), - errorMaxLines: 2, + return _validationFieldFillHeight( + TextFormField( + key: _fieldKey, + controller: widget.controller, + focusNode: _focusNode, + keyboardType: TextInputType.emailAddress, + autocorrect: false, + enableSuggestions: false, + autofillHints: const [AutofillHints.email], + textInputAction: TextInputAction.next, + textAlignVertical: TextAlignVertical.center, + inputFormatters: const [EmailMaxLengthFormatter()], + style: ValidationFormMetrics.fieldTextStyle, + decoration: + ValidationFieldDecoration.input(hint: widget.hintText).copyWith( + errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), + errorMaxLines: 2, + ), + validator: (value) => + validateEmail(value, allowEmpty: widget.allowEmpty), ), - validator: (value) => - validateEmail(value, allowEmpty: widget.allowEmpty), ); } } @@ -431,22 +531,25 @@ class _ValidationPostalCodeFieldState extends State { @override Widget build(BuildContext context) { - return TextFormField( - key: _fieldKey, - controller: widget.controller, - focusNode: _focusNode, - keyboardType: TextInputType.number, - textInputAction: TextInputAction.next, - inputFormatters: kFrenchPostalCodeInputFormatters, - style: const TextStyle(color: Colors.black87, fontSize: 14), - decoration: ValidationFieldDecoration.input( - hint: widget.hintText ?? '5 chiffres', - ).copyWith( - errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), - errorMaxLines: 2, + return _validationFieldFillHeight( + TextFormField( + key: _fieldKey, + controller: widget.controller, + focusNode: _focusNode, + keyboardType: TextInputType.number, + textInputAction: TextInputAction.next, + textAlignVertical: TextAlignVertical.center, + inputFormatters: kFrenchPostalCodeInputFormatters, + style: ValidationFormMetrics.fieldTextStyle, + decoration: ValidationFieldDecoration.input( + hint: widget.hintText ?? '5 chiffres', + ).copyWith( + errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), + errorMaxLines: 2, + ), + validator: (value) => + validateFrenchPostalCode(value, allowEmpty: widget.allowEmpty), ), - validator: (value) => - validateFrenchPostalCode(value, allowEmpty: widget.allowEmpty), ); } } @@ -506,20 +609,24 @@ class _ValidationPhoneFieldState extends State { @override Widget build(BuildContext context) { - return TextFormField( - key: _fieldKey, - controller: widget.controller, - focusNode: _focusNode, - keyboardType: TextInputType.phone, - textInputAction: TextInputAction.next, - inputFormatters: frenchPhoneInputFormatters, - style: const TextStyle(color: Colors.black87, fontSize: 14), - decoration: ValidationFieldDecoration.input(hint: widget.hintText).copyWith( - errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), - errorMaxLines: 2, + return _validationFieldFillHeight( + TextFormField( + key: _fieldKey, + controller: widget.controller, + focusNode: _focusNode, + keyboardType: TextInputType.phone, + textInputAction: TextInputAction.next, + textAlignVertical: TextAlignVertical.center, + inputFormatters: frenchPhoneInputFormatters, + style: ValidationFormMetrics.fieldTextStyle, + decoration: + ValidationFieldDecoration.input(hint: widget.hintText).copyWith( + errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), + errorMaxLines: 2, + ), + validator: (value) => + validateFrenchNationalPhone(value, allowEmpty: widget.allowEmpty), ), - validator: (value) => - validateFrenchNationalPhone(value, allowEmpty: widget.allowEmpty), ); } } @@ -591,23 +698,26 @@ class _ValidationNirFieldState extends State { @override Widget build(BuildContext context) { - return TextFormField( - key: _fieldKey, - controller: widget.controller, - focusNode: _focusNode, - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - autovalidateMode: AutovalidateMode.onUserInteraction, - inputFormatters: const [NirInputFormatter()], - style: const TextStyle(color: Colors.black87, fontSize: 14), - decoration: ValidationFieldDecoration.input( - hint: widget.hintText ?? '1 12 34 56 789 012 - 34', - ).copyWith( - errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), - errorMaxLines: 2, + return _validationFieldFillHeight( + TextFormField( + key: _fieldKey, + controller: widget.controller, + focusNode: _focusNode, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + textAlignVertical: TextAlignVertical.center, + autovalidateMode: AutovalidateMode.onUserInteraction, + inputFormatters: const [NirInputFormatter()], + style: ValidationFormMetrics.fieldTextStyle, + decoration: ValidationFieldDecoration.input( + hint: widget.hintText ?? '1 12 34 56 789 012 - 34', + ).copyWith( + errorStyle: TextStyle(color: Colors.red.shade700, fontSize: 11), + errorMaxLines: 2, + ), + onChanged: (_) => _fieldKey.currentState?.validate(), + validator: _validator, ), - onChanged: (_) => _fieldKey.currentState?.validate(), - validator: _validator, ); } } @@ -685,16 +795,19 @@ class _ValidationReadOnlyFieldState extends State { @override Widget build(BuildContext context) { if (!widget.compact && widget.maxLines == 1) { - return TextField( - controller: _controller, - readOnly: true, - enableInteractiveSelection: false, - style: TextStyle( - color: widget.error ? Colors.red.shade800 : Colors.black87, - fontSize: 14, - fontWeight: widget.error ? FontWeight.w600 : null, + return _validationFieldFillHeight( + TextField( + controller: _controller, + readOnly: true, + enableInteractiveSelection: false, + textAlignVertical: TextAlignVertical.center, + style: TextStyle( + color: widget.error ? Colors.red.shade800 : Colors.black87, + fontSize: ValidationFormMetrics.fieldTextFontSize, + fontWeight: widget.error ? FontWeight.w600 : null, + ), + decoration: ValidationFieldDecoration.readOnly(error: widget.error), ), - decoration: ValidationFieldDecoration.readOnly(error: widget.error), ); } diff --git a/frontend/lib/widgets/admin/validation_dossier_modal.dart b/frontend/lib/widgets/admin/validation_dossier_modal.dart index 88e2937..7bc3faf 100644 --- a/frontend/lib/widgets/admin/validation_dossier_modal.dart +++ b/frontend/lib/widgets/admin/validation_dossier_modal.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/dossier_unifie.dart'; import 'package:p_tits_pas/services/user_service.dart'; +import 'package:p_tits_pas/widgets/admin/am_dossier_wizard.dart'; import 'package:p_tits_pas/widgets/admin/validation_am_wizard.dart'; import 'package:p_tits_pas/widgets/admin/validation_family_wizard.dart'; @@ -76,8 +77,13 @@ class _ValidationDossierModalState extends State { /// Largeur modale = 1,5 × 620. static const double _modalWidth = 930; // 620 * 1.5 - // Hauteur uniforme (ajustée +5px pour éviter l'overflow des étapes parents sans scroll). - static const double _bodyHeight = 435; + static const double _familyBodyHeight = 435; + + double get _bodyHeight { + final d = _dossier; + if (d != null && d.isAm) return AmDossierWizard.shellBodyHeight; + return _familyBodyHeight; + } @override Widget build(BuildContext context) { diff --git a/frontend/lib/widgets/common/identity_block.dart b/frontend/lib/widgets/common/identity_block.dart index d42de66..2aee3e8 100644 --- a/frontend/lib/widgets/common/identity_block.dart +++ b/frontend/lib/widgets/common/identity_block.dart @@ -68,7 +68,9 @@ class IdentityValues { /// Bloc identité : Nom/Prénom, Tél/Email, Adresse, CP/Ville. /// Grille partagée (création de compte, validation AM/famille, fiches admin, etc.). -class IdentityBlock extends StatelessWidget { final String? title; +class IdentityBlock extends StatelessWidget { + final String? title; + final bool expandVertically; final String? _nom; final String? _prenom; @@ -92,6 +94,7 @@ class IdentityBlock extends StatelessWidget { final String? title; const IdentityBlock.readOnly({ super.key, this.title, + this.expandVertically = false, required String nom, required String prenom, required String telephone, @@ -117,6 +120,7 @@ class IdentityBlock extends StatelessWidget { final String? title; const IdentityBlock.editable({ super.key, this.title, + this.expandVertically = false, required TextEditingController nomController, required TextEditingController prenomController, required TextEditingController telephoneController, @@ -143,11 +147,13 @@ class IdentityBlock extends StatelessWidget { final String? title; factory IdentityBlock.readOnlyValues({ Key? key, String? title, + bool expandVertically = false, required IdentityValues values, }) { return IdentityBlock.readOnly( key: key, title: title, + expandVertically: expandVertically, nom: values.nom, prenom: values.prenom, telephone: values.telephone, @@ -164,10 +170,12 @@ class IdentityBlock extends StatelessWidget { final String? title; Key? key, String? title, String emptyLabel = 'Non défini', + bool expandVertically = false, }) { return IdentityBlock.readOnlyValues( key: key, title: title, + expandVertically: expandVertically, values: IdentityValues.fromUser(user, emptyLabel: emptyLabel), ); } @@ -197,6 +205,7 @@ class IdentityBlock extends StatelessWidget { final String? title; title: title, rowLayout: rowLayout, rowFlex: rowFlex, + expandVertically: expandVertically, fields: [ ValidationLabeledField( label: 'Nom', @@ -243,6 +252,7 @@ class IdentityBlock extends StatelessWidget { final String? title; title: title, rowLayout: rowLayout, rowFlex: rowFlex, + expandVertically: expandVertically, fields: [ ValidationLabeledField( label: 'Nom',