fix(#131): polish fiche AM — champs pro, places, vigilance et photo.
Fiche pro entièrement éditable, places déclarées en lecture seule avec alerte rouge, icône vigilance sur la liste AM, et cadrage photo aligné sur le wizard validation. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
479a32b4bf
commit
4985726bc6
@ -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];
|
||||
}
|
||||
|
||||
45
frontend/lib/utils/am_vigilance.dart
Normal file
45
frontend/lib/utils/am_vigilance.dart
Normal file
@ -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.';
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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}',
|
||||
|
||||
@ -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<AdminAmEditModal>
|
||||
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<AdminAmEditModal>
|
||||
_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<AdminAmEditModal>
|
||||
_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<AdminAmEditModal>
|
||||
|
||||
void _onNameFieldChanged() => setState(() {});
|
||||
|
||||
void _onCapacityChanged() => setState(() {});
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tabCtrl.dispose();
|
||||
@ -136,8 +158,12 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
_villeCtrl,
|
||||
_cpCtrl,
|
||||
_agrementCtrl,
|
||||
_nirCtrl,
|
||||
_dateNaissanceCtrl,
|
||||
_lieuNaissanceVilleCtrl,
|
||||
_lieuNaissancePaysCtrl,
|
||||
_dateAgrementCtrl,
|
||||
_capaciteCtrl,
|
||||
_placesCtrl,
|
||||
]) {
|
||||
c.dispose();
|
||||
}
|
||||
@ -175,11 +201,43 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
|
||||
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<AdminAmEditModal>
|
||||
'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<AdminAmEditModal>
|
||||
}
|
||||
}
|
||||
|
||||
String _v(String? s) =>
|
||||
(s != null && s.trim().isNotEmpty) ? s.trim() : '–';
|
||||
|
||||
Widget _identityTab() {
|
||||
return SingleChildScrollView(
|
||||
@ -407,7 +467,6 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
}
|
||||
|
||||
Widget _proFieldsGrid() {
|
||||
final u = widget.assistante.user;
|
||||
return ValidationFormGrid(
|
||||
title: 'Dossier professionnel',
|
||||
rowLayout: _photoProRowLayout,
|
||||
@ -415,29 +474,34 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
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<AdminAmEditModal>
|
||||
),
|
||||
ValidationLabeledField(
|
||||
label: 'Date d\'agrément',
|
||||
field: ValidationReadOnlyField(
|
||||
value: formatIsoDateFr(widget.assistante.agreementDate),
|
||||
field: ValidationEditableField(
|
||||
controller: _dateAgrementCtrl,
|
||||
compact: true,
|
||||
hintText: 'jj/mm/aaaa',
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -513,7 +578,11 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
}
|
||||
|
||||
Widget _childrenCapacityFields() {
|
||||
return ValidationEditableSection(
|
||||
final inconsistent = _placesInconsistent();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
ValidationEditableSection(
|
||||
compact: true,
|
||||
rowLayout: const [2],
|
||||
fields: [
|
||||
@ -528,29 +597,30 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
),
|
||||
ValidationLabeledField(
|
||||
label: 'Places disponibles',
|
||||
field: ValidationEditableField(
|
||||
controller: _placesCtrl,
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
field: ValidationReadOnlyField(
|
||||
value: _placesDisplayValue(),
|
||||
compact: true,
|
||||
error: inconsistent,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
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: [
|
||||
@ -572,7 +642,6 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: AdminChildrenAffiliationPanel(
|
||||
height: listHeight,
|
||||
children: _children,
|
||||
scrollController: _childrenScrollCtrl,
|
||||
onOpen: _openChild,
|
||||
@ -582,8 +651,6 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFooter() {
|
||||
|
||||
@ -36,7 +36,11 @@ class AdminAmPhotoFrame extends StatelessWidget {
|
||||
ph = pw / ar;
|
||||
}
|
||||
|
||||
return Container(
|
||||
// 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),
|
||||
@ -45,8 +49,6 @@ class AdminAmPhotoFrame extends StatelessWidget {
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(uniformFrame),
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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<AdminUserCard> {
|
||||
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: [
|
||||
|
||||
@ -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: 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: ValidationFieldDecoration.input(
|
||||
hint: hintText,
|
||||
compact: true,
|
||||
).copyWith(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 9),
|
||||
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,
|
||||
|
||||
@ -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<AuthNetworkImage> {
|
||||
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<AuthNetworkImage> {
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
fit: widget.fit,
|
||||
alignment: widget.alignment,
|
||||
headers: headers,
|
||||
loadingBuilder: widget.loadingBuilder,
|
||||
errorBuilder: err,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user