diff --git a/frontend/lib/services/user_service.dart b/frontend/lib/services/user_service.dart index 3aa8111..31ea722 100644 --- a/frontend/lib/services/user_service.dart +++ b/frontend/lib/services/user_service.dart @@ -575,6 +575,9 @@ class UserService { 'ville', 'code_postal', 'statut', + 'date_naissance', + 'lieu_naissance_ville', + 'lieu_naissance_pays', ]) { if (body.containsKey(k)) userFields[k] = body[k]; } diff --git a/frontend/lib/utils/am_vigilance.dart b/frontend/lib/utils/am_vigilance.dart new file mode 100644 index 0000000..944dc93 --- /dev/null +++ b/frontend/lib/utils/am_vigilance.dart @@ -0,0 +1,45 @@ +import 'package:p_tits_pas/models/assistante_maternelle_model.dart'; + +/// Places disponibles attendues : capacité max − enfants rattachés. +int? amExpectedPlacesAvailable({ + required int? maxChildren, + required int childrenCount, +}) { + if (maxChildren == null) return null; + return (maxChildren - childrenCount).clamp(0, maxChildren); +} + +/// True si la valeur déclarée par l'AM ne correspond pas au calcul métier. +bool amHasPlacesInconsistency({ + required int? maxChildren, + required int? placesAvailable, + required int childrenCount, +}) { + final expected = amExpectedPlacesAvailable( + maxChildren: maxChildren, + childrenCount: childrenCount, + ); + if (expected == null) return false; + if (placesAvailable == null) return childrenCount > (maxChildren ?? 0); + return placesAvailable != expected; +} + +String? amPlacesVigilanceMessage(AssistanteMaternelleModel am) { + if (!amHasPlacesInconsistency( + maxChildren: am.maxChildren, + placesAvailable: am.placesAvailable, + childrenCount: am.children.length, + )) { + return null; + } + final stored = am.placesAvailable; + final expected = amExpectedPlacesAvailable( + maxChildren: am.maxChildren, + childrenCount: am.children.length, + ); + final storedLabel = stored?.toString() ?? 'non renseigné'; + final expectedLabel = expected?.toString() ?? '–'; + return 'Point de vigilance : l\'AM déclare $storedLabel place(s) disponible(s), ' + 'alors que capacité ${am.maxChildren ?? '–'} − ' + '${am.children.length} enfant(s) rattaché(s) = $expectedLabel.'; +} diff --git a/frontend/lib/utils/date_display_utils.dart b/frontend/lib/utils/date_display_utils.dart index 81318e3..9836e82 100644 --- a/frontend/lib/utils/date_display_utils.dart +++ b/frontend/lib/utils/date_display_utils.dart @@ -10,7 +10,25 @@ String formatIsoDateFr(String? s, {String ifEmpty = '–'}) { } } -/// Parties d'âge calendaire entre [birth] et [reference] (date-only). +/// Convertit `dd/MM/yyyy` (ou ISO) en `yyyy-MM-dd` pour l'API. +String? parseFrDateToIso(String text) { + final t = text.trim(); + if (t.isEmpty) return null; + try { + return DateFormat('dd/MM/yyyy') + .parseStrict(t) + .toIso8601String() + .split('T') + .first; + } catch (_) { + try { + return DateTime.parse(t).toIso8601String().split('T').first; + } catch (_) { + return null; + } + } +} + ({int years, int months, int days}) computeChildAgeParts( DateTime birth, DateTime reference, diff --git a/frontend/lib/widgets/admin/assistante_maternelle_management_widget.dart b/frontend/lib/widgets/admin/assistante_maternelle_management_widget.dart index cfc7826..5ee5ed1 100644 --- a/frontend/lib/widgets/admin/assistante_maternelle_management_widget.dart +++ b/frontend/lib/widgets/admin/assistante_maternelle_management_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/assistante_maternelle_model.dart'; +import 'package:p_tits_pas/utils/am_vigilance.dart'; import 'package:p_tits_pas/services/user_service.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_am_edit_modal.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart'; @@ -76,10 +77,12 @@ class _AssistanteMaternelleManagementWidgetState itemCount: filteredAssistantes.length, itemBuilder: (context, index) { final assistante = filteredAssistantes[index]; + final vigilance = amPlacesVigilanceMessage(assistante); return AdminUserCard( title: assistante.user.fullName, avatarUrl: assistante.user.photoUrl, fallbackIcon: Icons.face, + vigilanceTooltip: vigilance, subtitleLines: [ assistante.user.email, 'Zone : ${assistante.residenceCity ?? 'N/A'} | Capacité : ${assistante.maxChildren ?? 0}', diff --git a/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart b/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart index 55d6de9..95e85e2 100644 --- a/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart +++ b/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:p_tits_pas/models/assistante_maternelle_model.dart'; import 'package:p_tits_pas/models/enfant_admin_model.dart'; import 'package:p_tits_pas/models/parent_child_summary.dart'; +import 'package:p_tits_pas/utils/am_vigilance.dart'; import 'package:p_tits_pas/utils/date_display_utils.dart'; import 'package:p_tits_pas/utils/nir_utils.dart'; import 'package:p_tits_pas/utils/phone_utils.dart'; @@ -41,8 +42,12 @@ class _AdminAmEditModalState extends State late final TextEditingController _villeCtrl; late final TextEditingController _cpCtrl; late final TextEditingController _agrementCtrl; + late final TextEditingController _nirCtrl; + late final TextEditingController _dateNaissanceCtrl; + late final TextEditingController _lieuNaissanceVilleCtrl; + late final TextEditingController _lieuNaissancePaysCtrl; + late final TextEditingController _dateAgrementCtrl; late final TextEditingController _capaciteCtrl; - late final TextEditingController _placesCtrl; late String _statut; late bool _disponible; @@ -87,12 +92,22 @@ class _AdminAmEditModalState extends State _villeCtrl = TextEditingController(text: u.ville ?? ''); _cpCtrl = TextEditingController(text: u.codePostal ?? ''); _agrementCtrl = TextEditingController(text: am.approvalNumber ?? ''); + _nirCtrl = TextEditingController(text: _formatNirDisplay()); + _dateNaissanceCtrl = TextEditingController( + text: formatIsoDateFr(u.dateNaissance, ifEmpty: ''), + ); + _lieuNaissanceVilleCtrl = TextEditingController( + text: u.lieuNaissanceVille ?? '', + ); + _lieuNaissancePaysCtrl = TextEditingController( + text: u.lieuNaissancePays ?? '', + ); + _dateAgrementCtrl = TextEditingController( + text: formatIsoDateFr(am.agreementDate, ifEmpty: ''), + ); _capaciteCtrl = TextEditingController( text: am.maxChildren?.toString() ?? '', ); - _placesCtrl = TextEditingController( - text: am.placesAvailable?.toString() ?? '', - ); _statut = u.statut ?? 'en_attente'; _disponible = am.available ?? true; _children = List.of(am.children); @@ -107,11 +122,16 @@ class _AdminAmEditModalState extends State _villeCtrl, _cpCtrl, _agrementCtrl, + _nirCtrl, + _dateNaissanceCtrl, + _lieuNaissanceVilleCtrl, + _lieuNaissancePaysCtrl, + _dateAgrementCtrl, _capaciteCtrl, - _placesCtrl, ]) { c.addListener(_markDirty); } + _capaciteCtrl.addListener(_onCapacityChanged); _nomCtrl.addListener(_onNameFieldChanged); _prenomCtrl.addListener(_onNameFieldChanged); _tabCtrl.addListener(_onTabChanged); @@ -124,6 +144,8 @@ class _AdminAmEditModalState extends State void _onNameFieldChanged() => setState(() {}); + void _onCapacityChanged() => setState(() {}); + @override void dispose() { _tabCtrl.dispose(); @@ -136,8 +158,12 @@ class _AdminAmEditModalState extends State _villeCtrl, _cpCtrl, _agrementCtrl, + _nirCtrl, + _dateNaissanceCtrl, + _lieuNaissanceVilleCtrl, + _lieuNaissancePaysCtrl, + _dateAgrementCtrl, _capaciteCtrl, - _placesCtrl, ]) { c.dispose(); } @@ -175,11 +201,43 @@ class _AdminAmEditModalState extends State String _formatNirDisplay() { final raw = widget.assistante.nir?.trim() ?? ''; - if (raw.isEmpty) return '–'; + if (raw.isEmpty) return ''; final digits = nirToRaw(raw).toUpperCase(); return digits.length == 15 ? formatNir(digits) : raw; } + String? _frDateToIso(String text) => parseFrDateToIso(text); + + int? _capaciteMax() => _parseIntField(_capaciteCtrl); + + int? _computedPlacesAvailable() => amExpectedPlacesAvailable( + maxChildren: _capaciteMax(), + childrenCount: _children.length, + ); + + bool _placesInconsistent() => amHasPlacesInconsistency( + maxChildren: _capaciteMax(), + placesAvailable: widget.assistante.placesAvailable, + childrenCount: _children.length, + ); + + String _placesDisplayValue() { + final stored = widget.assistante.placesAvailable; + if (stored != null) return stored.toString(); + return '–'; + } + + String? _placesInconsistencyMessage() { + if (!_placesInconsistent()) return null; + final stored = widget.assistante.placesAvailable; + final expected = _computedPlacesAvailable(); + final storedLabel = stored?.toString() ?? 'non renseigné'; + final expectedLabel = expected?.toString() ?? '–'; + return 'Incohérence : l\'AM déclare $storedLabel place(s) disponible(s), ' + 'le calcul (capacité ${_capaciteMax() ?? '–'} − ${_children.length} ' + 'enfant(s) rattaché(s)) donne $expectedLabel.'; + } + int? _parseIntField(TextEditingController c) { final t = c.text.trim(); if (t.isEmpty) return null; @@ -223,10 +281,14 @@ class _AdminAmEditModalState extends State 'code_postal': _cpCtrl.text.trim(), 'statut': _statut, 'approval_number': _agrementCtrl.text.trim(), - if (_parseIntField(_capaciteCtrl) != null) - 'max_children': _parseIntField(_capaciteCtrl), - if (_parseIntField(_placesCtrl) != null) - 'places_available': _parseIntField(_placesCtrl), + 'nir': nirToRaw(_nirCtrl.text), + if (_frDateToIso(_dateNaissanceCtrl.text) != null) + 'date_naissance': _frDateToIso(_dateNaissanceCtrl.text), + 'lieu_naissance_ville': _lieuNaissanceVilleCtrl.text.trim(), + 'lieu_naissance_pays': _lieuNaissancePaysCtrl.text.trim(), + if (_frDateToIso(_dateAgrementCtrl.text) != null) + 'agreement_date': _frDateToIso(_dateAgrementCtrl.text), + if (_capaciteMax() != null) 'max_children': _capaciteMax(), 'available': _disponible, }, ); @@ -388,8 +450,6 @@ class _AdminAmEditModalState extends State } } - String _v(String? s) => - (s != null && s.trim().isNotEmpty) ? s.trim() : '–'; Widget _identityTab() { return SingleChildScrollView( @@ -407,7 +467,6 @@ class _AdminAmEditModalState extends State } Widget _proFieldsGrid() { - final u = widget.assistante.user; return ValidationFormGrid( title: 'Dossier professionnel', rowLayout: _photoProRowLayout, @@ -415,29 +474,34 @@ class _AdminAmEditModalState extends State fields: [ ValidationLabeledField( label: 'NIR', - field: ValidationReadOnlyField( - value: _formatNirDisplay(), + field: ValidationEditableField( + controller: _nirCtrl, compact: true, + hintText: '15 chiffres', + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[\d\s]')), + ], ), ), ValidationLabeledField( label: 'Date de naissance', - field: ValidationReadOnlyField( - value: formatIsoDateFr(u.dateNaissance), + field: ValidationEditableField( + controller: _dateNaissanceCtrl, compact: true, + hintText: 'jj/mm/aaaa', ), ), ValidationLabeledField( label: 'Ville de naissance', - field: ValidationReadOnlyField( - value: _v(u.lieuNaissanceVille), + field: ValidationEditableField( + controller: _lieuNaissanceVilleCtrl, compact: true, ), ), ValidationLabeledField( label: 'Pays de naissance', - field: ValidationReadOnlyField( - value: _v(u.lieuNaissancePays), + field: ValidationEditableField( + controller: _lieuNaissancePaysCtrl, compact: true, ), ), @@ -450,9 +514,10 @@ class _AdminAmEditModalState extends State ), ValidationLabeledField( label: 'Date d\'agrément', - field: ValidationReadOnlyField( - value: formatIsoDateFr(widget.assistante.agreementDate), + field: ValidationEditableField( + controller: _dateAgrementCtrl, compact: true, + hintText: 'jj/mm/aaaa', ), ), ], @@ -513,76 +578,78 @@ class _AdminAmEditModalState extends State } Widget _childrenCapacityFields() { - return ValidationEditableSection( - compact: true, - rowLayout: const [2], - fields: [ - ValidationLabeledField( - label: 'Capacité max (enfants)', - field: ValidationEditableField( - controller: _capaciteCtrl, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - compact: true, - ), + final inconsistent = _placesInconsistent(); + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ValidationEditableSection( + compact: true, + rowLayout: const [2], + fields: [ + ValidationLabeledField( + label: 'Capacité max (enfants)', + field: ValidationEditableField( + controller: _capaciteCtrl, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + compact: true, + ), + ), + ValidationLabeledField( + label: 'Places disponibles', + field: ValidationReadOnlyField( + value: _placesDisplayValue(), + compact: true, + error: inconsistent, + ), + ), + ], ), - ValidationLabeledField( - label: 'Places disponibles', - field: ValidationEditableField( - controller: _placesCtrl, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - compact: true, + if (inconsistent) ...[ + const SizedBox(height: 6), + Text( + _placesInconsistencyMessage()!, + style: TextStyle( + fontSize: 12, + color: Colors.red.shade700, + height: 1.3, + ), ), - ), + ], ], ); } Widget _childrenTab() { - return LayoutBuilder( - builder: (context, constraints) { - const headerBlock = 88.0; - const capacityBlock = 56.0; - const gaps = 20.0; - final listHeight = (constraints.maxHeight - - headerBlock - - capacityBlock - - gaps) - .clamp(72.0, AdminChildrenAffiliationPanel.defaultViewportHeight); - - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - _childrenCapacityFields(), - const SizedBox(height: 10), - Text( - 'Enfants accueillis : ${_children.length}', - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w600, - color: Colors.black87, - ), - ), - const SizedBox(height: 2), - Text( - 'Enfants rattachés à cette assistante (accueil / garde).', - style: TextStyle(fontSize: 12, color: Colors.grey.shade700), - ), - const SizedBox(height: 8), - Expanded( - child: AdminChildrenAffiliationPanel( - height: listHeight, - children: _children, - scrollController: _childrenScrollCtrl, - onOpen: _openChild, - onDetach: _detachChild, - emptyMessage: 'Aucun enfant rattaché à cette assistante', - ), - ), - ], - ); - }, + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _childrenCapacityFields(), + const SizedBox(height: 10), + Text( + 'Enfants accueillis : ${_children.length}', + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + color: Colors.black87, + ), + ), + const SizedBox(height: 2), + Text( + 'Enfants rattachés à cette assistante (accueil / garde).', + style: TextStyle(fontSize: 12, color: Colors.grey.shade700), + ), + const SizedBox(height: 8), + Expanded( + child: AdminChildrenAffiliationPanel( + children: _children, + scrollController: _childrenScrollCtrl, + onOpen: _openChild, + onDetach: _detachChild, + emptyMessage: 'Aucun enfant rattaché à cette assistante', + ), + ), + ], ); } diff --git a/frontend/lib/widgets/admin/common/admin_am_photo_frame.dart b/frontend/lib/widgets/admin/common/admin_am_photo_frame.dart index 9d53a7f..17ac62e 100644 --- a/frontend/lib/widgets/admin/common/admin_am_photo_frame.dart +++ b/frontend/lib/widgets/admin/common/admin_am_photo_frame.dart @@ -36,17 +36,19 @@ class AdminAmPhotoFrame extends StatelessWidget { ph = pw / ar; } - return Container( - decoration: BoxDecoration( - color: Colors.grey.shade100, - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey.shade300), - ), - clipBehavior: Clip.antiAlias, - child: Padding( - padding: const EdgeInsets.all(uniformFrame), - child: Align( - alignment: Alignment.topCenter, + // Cadre gris = taille photo + padding uniforme ; centré dans la colonne + // (évite le vide blanc en bas quand le conteneur parent est plus haut). + return Align( + alignment: Alignment.topCenter, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.shade100, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.grey.shade300), + ), + clipBehavior: Clip.antiAlias, + child: Padding( + padding: const EdgeInsets.all(uniformFrame), child: ClipRRect( borderRadius: BorderRadius.circular(6), child: SizedBox( @@ -84,6 +86,7 @@ class AdminAmPhotoFrame extends StatelessWidget { width: pw, height: ph, fit: BoxFit.cover, + alignment: Alignment.topCenter, loadingBuilder: (_, child, progress) { if (progress == null) return child; return ColoredBox( diff --git a/frontend/lib/widgets/admin/common/admin_children_affiliation_panel.dart b/frontend/lib/widgets/admin/common/admin_children_affiliation_panel.dart index 8a534bb..e984e66 100644 --- a/frontend/lib/widgets/admin/common/admin_children_affiliation_panel.dart +++ b/frontend/lib/widgets/admin/common/admin_children_affiliation_panel.dart @@ -26,7 +26,20 @@ class AdminChildrenAffiliationPanel extends StatelessWidget { @override Widget build(BuildContext context) { - final panelHeight = height ?? defaultViewportHeight; + if (height != null) { + return _panel(height!); + } + return LayoutBuilder( + builder: (context, constraints) { + final h = constraints.maxHeight.isFinite && constraints.maxHeight > 0 + ? constraints.maxHeight + : defaultViewportHeight; + return _panel(h); + }, + ); + } + + Widget _panel(double panelHeight) { return Container( height: panelHeight, decoration: BoxDecoration( diff --git a/frontend/lib/widgets/admin/common/admin_user_card.dart b/frontend/lib/widgets/admin/common/admin_user_card.dart index 0e6c5ab..7caafe1 100644 --- a/frontend/lib/widgets/admin/common/admin_user_card.dart +++ b/frontend/lib/widgets/admin/common/admin_user_card.dart @@ -12,6 +12,7 @@ class AdminUserCard extends StatefulWidget { final Color? backgroundColor; final Color? titleColor; final Color? infoColor; + final String? vigilanceTooltip; const AdminUserCard({ super.key, @@ -24,6 +25,7 @@ class AdminUserCard extends StatefulWidget { this.backgroundColor, this.titleColor, this.infoColor, + this.vigilanceTooltip, }); @override @@ -65,6 +67,17 @@ class _AdminUserCardState extends State { children: [ _buildAvatar(avatarUrl), const SizedBox(width: 10), + if (widget.vigilanceTooltip != null) ...[ + Tooltip( + message: widget.vigilanceTooltip!, + child: Icon( + Icons.error_outline, + size: 20, + color: Colors.orange.shade800, + ), + ), + const SizedBox(width: 6), + ], Expanded( child: Row( children: [ diff --git a/frontend/lib/widgets/admin/common/validation_detail_section.dart b/frontend/lib/widgets/admin/common/validation_detail_section.dart index 24974c0..0ece3fd 100644 --- a/frontend/lib/widgets/admin/common/validation_detail_section.dart +++ b/frontend/lib/widgets/admin/common/validation_detail_section.dart @@ -210,6 +210,28 @@ class ValidationEditableField extends StatelessWidget { static const double _compactFieldHeight = 34; + static BoxDecoration _compactDecoration({bool error = false}) { + return BoxDecoration( + color: error ? Colors.red.shade50 : Colors.grey.shade50, + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: error ? Colors.red.shade400 : Colors.grey.shade300, + ), + ); + } + + static InputDecoration _compactInputDecoration({String? hint}) { + return InputDecoration( + isDense: true, + filled: false, + hintText: hint, + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 9), + ); + } + @override Widget build(BuildContext context) { if (!compact || maxLines > 1) { @@ -230,17 +252,19 @@ class ValidationEditableField extends StatelessWidget { } 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), + child: DecoratedBox( + decoration: _compactDecoration(), + child: TextField( + controller: controller, + keyboardType: keyboardType, + inputFormatters: inputFormatters, + maxLines: 1, + style: const TextStyle( + color: Colors.black87, + fontSize: 13, + height: 1.15, + ), + decoration: _compactInputDecoration(hint: hintText), ), ), ); @@ -278,12 +302,14 @@ class ValidationReadOnlyField extends StatelessWidget { final String value; final int? maxLines; final bool compact; + final bool error; const ValidationReadOnlyField({ super.key, required this.value, this.maxLines = 1, this.compact = false, + this.error = false, }); static const double _compactFieldHeight = 34; @@ -298,13 +324,20 @@ class ValidationReadOnlyField extends StatelessWidget { horizontal: compact ? 10 : 12, vertical: compact ? 7 : 10, ), - decoration: ValidationFieldDecoration.container(), + decoration: error + ? BoxDecoration( + color: Colors.red.shade50, + borderRadius: BorderRadius.circular(6), + border: Border.all(color: Colors.red.shade400), + ) + : ValidationFieldDecoration.container(), child: Text( value, style: TextStyle( - color: Colors.black87, + color: error ? Colors.red.shade800 : Colors.black87, fontSize: compact ? 13 : 14, height: compact ? 1.2 : null, + fontWeight: error ? FontWeight.w600 : null, ), maxLines: maxLines, overflow: TextOverflow.ellipsis, diff --git a/frontend/lib/widgets/common/auth_network_image.dart b/frontend/lib/widgets/common/auth_network_image.dart index 60ff0f2..10221ed 100644 --- a/frontend/lib/widgets/common/auth_network_image.dart +++ b/frontend/lib/widgets/common/auth_network_image.dart @@ -13,6 +13,7 @@ class AuthNetworkImage extends StatefulWidget { this.width, this.height, this.fit = BoxFit.cover, + this.alignment = Alignment.center, this.loadingBuilder, this.errorBuilder, }); @@ -21,6 +22,7 @@ class AuthNetworkImage extends StatefulWidget { final double? width; final double? height; final BoxFit fit; + final AlignmentGeometry alignment; final ImageLoadingBuilder? loadingBuilder; final ImageErrorWidgetBuilder? errorBuilder; @@ -75,6 +77,7 @@ class _AuthNetworkImageState extends State { width: widget.width, height: widget.height, fit: widget.fit, + alignment: widget.alignment, loadingBuilder: widget.loadingBuilder, errorBuilder: err, ); @@ -105,6 +108,7 @@ class _AuthNetworkImageState extends State { width: widget.width, height: widget.height, fit: widget.fit, + alignment: widget.alignment, headers: headers, loadingBuilder: widget.loadingBuilder, errorBuilder: err,