Merge branch 'feature/147-modale-selection-am-enfant' into develop
Modale de sélection d'AM (#147) depuis la fiche enfant.
This commit is contained in:
commit
f74b14c203
@ -8,6 +8,7 @@ import 'package:p_tits_pas/models/pending_family.dart';
|
|||||||
import 'package:p_tits_pas/models/dossier_unifie.dart';
|
import 'package:p_tits_pas/models/dossier_unifie.dart';
|
||||||
import 'package:p_tits_pas/services/api/api_config.dart';
|
import 'package:p_tits_pas/services/api/api_config.dart';
|
||||||
import 'package:p_tits_pas/services/api/tokenService.dart';
|
import 'package:p_tits_pas/services/api/tokenService.dart';
|
||||||
|
import 'package:p_tits_pas/utils/am_vigilance.dart';
|
||||||
|
|
||||||
class DocumentActifInfo {
|
class DocumentActifInfo {
|
||||||
final String id;
|
final String id;
|
||||||
@ -688,7 +689,24 @@ class UserService {
|
|||||||
if (response.statusCode != 200 && response.statusCode != 201) {
|
if (response.statusCode != 200 && response.statusCode != 201) {
|
||||||
throw Exception(_extractErrorMessage(response.body, 'Erreur rattachement enfant'));
|
throw Exception(_extractErrorMessage(response.body, 'Erreur rattachement enfant'));
|
||||||
}
|
}
|
||||||
return _amModelFromBody(response.body);
|
final am = _amModelFromBody(response.body);
|
||||||
|
// Recalcule toujours places_available (capacité − enfants) après attachement.
|
||||||
|
return syncAmPlacesAvailable(am);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Aligne `places_available` sur capacité − enfants rattachés.
|
||||||
|
static Future<AssistanteMaternelleModel> syncAmPlacesAvailable(
|
||||||
|
AssistanteMaternelleModel am,
|
||||||
|
) async {
|
||||||
|
final expected = amExpectedPlacesAvailable(
|
||||||
|
maxChildren: am.maxChildren,
|
||||||
|
childrenCount: am.children.length,
|
||||||
|
);
|
||||||
|
if (expected == null || am.placesAvailable == expected) return am;
|
||||||
|
return updateAmFiche(
|
||||||
|
amUserId: am.user.id,
|
||||||
|
body: {'places_available': expected},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<AssistanteMaternelleModel> detachEnfantFromAm({
|
static Future<AssistanteMaternelleModel> detachEnfantFromAm({
|
||||||
|
|||||||
@ -9,6 +9,18 @@ int? amExpectedPlacesAvailable({
|
|||||||
return (maxChildren - childrenCount).clamp(0, maxChildren);
|
return (maxChildren - childrenCount).clamp(0, maxChildren);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// True s'il reste au moins une place d'accueil (capacité − enfants).
|
||||||
|
bool amHasFreePlace(AssistanteMaternelleModel am) {
|
||||||
|
final expected = amExpectedPlacesAvailable(
|
||||||
|
maxChildren: am.maxChildren,
|
||||||
|
childrenCount: am.children.length,
|
||||||
|
);
|
||||||
|
if (expected != null) return expected > 0;
|
||||||
|
final places = am.placesAvailable;
|
||||||
|
if (places != null) return places > 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// True si la valeur déclarée par l'AM ne correspond pas au calcul métier.
|
/// True si la valeur déclarée par l'AM ne correspond pas au calcul métier.
|
||||||
bool amHasPlacesInconsistency({
|
bool amHasPlacesInconsistency({
|
||||||
required int? maxChildren,
|
required int? maxChildren,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import 'package:p_tits_pas/utils/enfant_status_utils.dart';
|
|||||||
import 'package:p_tits_pas/widgets/admin/common/admin_am_edit_modal.dart';
|
import 'package:p_tits_pas/widgets/admin/common/admin_am_edit_modal.dart';
|
||||||
import 'package:p_tits_pas/widgets/admin/common/admin_am_photo_frame.dart';
|
import 'package:p_tits_pas/widgets/admin/common/admin_am_photo_frame.dart';
|
||||||
import 'package:p_tits_pas/widgets/admin/common/admin_parent_edit_modal.dart';
|
import 'package:p_tits_pas/widgets/admin/common/admin_parent_edit_modal.dart';
|
||||||
|
import 'package:p_tits_pas/widgets/admin/common/admin_select_am_modal.dart';
|
||||||
import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart';
|
import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart';
|
||||||
import 'package:p_tits_pas/widgets/admin/validation_modal_theme.dart';
|
import 'package:p_tits_pas/widgets/admin/validation_modal_theme.dart';
|
||||||
import 'package:p_tits_pas/widgets/common/auth_network_image.dart';
|
import 'package:p_tits_pas/widgets/common/auth_network_image.dart';
|
||||||
@ -399,49 +400,25 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _attachAm() async {
|
Future<void> _attachAm() async {
|
||||||
List<AssistanteMaternelleModel> all;
|
|
||||||
try {
|
|
||||||
all = await UserService.getAssistantesMaternelles();
|
|
||||||
} catch (e) {
|
|
||||||
if (!mounted) return;
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(content: Text(e.toString().replaceFirst('Exception: ', ''))),
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_linkedAm != null && !_isSansGarde) {
|
if (_linkedAm != null && !_isSansGarde) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Détachez l\'assistante actuelle avant d\'en choisir une autre'),
|
content: Text(
|
||||||
|
'Détachez l\'assistante actuelle avant d\'en choisir une autre',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all.isEmpty) {
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
final selected = await AdminSelectAmModal.show(
|
||||||
const SnackBar(content: Text('Aucune assistante maternelle disponible')),
|
context,
|
||||||
);
|
excludeIds: {
|
||||||
return;
|
if (_linkedAm != null) _linkedAm!.user.id,
|
||||||
}
|
},
|
||||||
|
title: 'Choisir une assistante maternelle',
|
||||||
if (!mounted) return;
|
|
||||||
final selected = await showDialog<AssistanteMaternelleModel>(
|
|
||||||
context: context,
|
|
||||||
builder: (ctx) => SimpleDialog(
|
|
||||||
title: const Text('Choisir une assistante maternelle'),
|
|
||||||
children: all
|
|
||||||
.map(
|
|
||||||
(am) => SimpleDialogOption(
|
|
||||||
onPressed: () => Navigator.pop(ctx, am),
|
|
||||||
child: Text(am.user.fullName),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (selected == null || !mounted) return;
|
if (selected == null || !mounted) return;
|
||||||
|
|
||||||
|
|||||||
218
frontend/lib/widgets/admin/common/admin_select_am_modal.dart
Normal file
218
frontend/lib/widgets/admin/common/admin_select_am_modal.dart
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:p_tits_pas/models/assistante_maternelle_model.dart';
|
||||||
|
import 'package:p_tits_pas/services/user_service.dart';
|
||||||
|
import 'package:p_tits_pas/utils/am_vigilance.dart';
|
||||||
|
import 'package:p_tits_pas/widgets/admin/common/admin_am_edit_modal.dart';
|
||||||
|
import 'package:p_tits_pas/widgets/admin/common/admin_select_list_modal.dart';
|
||||||
|
import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart';
|
||||||
|
import 'package:p_tits_pas/widgets/admin/validation_modal_theme.dart';
|
||||||
|
|
||||||
|
List<String> _amSelectSubtitleLines(AssistanteMaternelleModel am) {
|
||||||
|
final lines = <String>[];
|
||||||
|
final zone = (am.residenceCity ?? '').trim();
|
||||||
|
if (zone.isNotEmpty) lines.add('Zone : $zone');
|
||||||
|
final agrement = (am.approvalNumber ?? '').trim();
|
||||||
|
if (agrement.isNotEmpty) lines.add('Agrément : $agrement');
|
||||||
|
final max = am.maxChildren;
|
||||||
|
final free = amExpectedPlacesAvailable(
|
||||||
|
maxChildren: max,
|
||||||
|
childrenCount: am.children.length,
|
||||||
|
) ??
|
||||||
|
am.placesAvailable;
|
||||||
|
if (free != null || max != null) {
|
||||||
|
lines.add('Places libres : ${free ?? '–'} / capa. ${max ?? '–'}');
|
||||||
|
}
|
||||||
|
lines.add('${am.children.length} enfant(s)');
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sélection d'une AM à rattacher (fiche enfant) — ticket #147.
|
||||||
|
/// S'appuie sur [AdminSelectListModal] (shell partagé avec #146).
|
||||||
|
class AdminSelectAmModal {
|
||||||
|
AdminSelectAmModal._();
|
||||||
|
|
||||||
|
static Future<AssistanteMaternelleModel?> show(
|
||||||
|
BuildContext context, {
|
||||||
|
Set<String> excludeIds = const {},
|
||||||
|
String title = 'Choisir une assistante maternelle',
|
||||||
|
}) {
|
||||||
|
return AdminSelectListModal.show<AssistanteMaternelleModel>(
|
||||||
|
context,
|
||||||
|
title: title,
|
||||||
|
searchHint: 'Rechercher par nom, prénom ou zone…',
|
||||||
|
emptyMessage: 'Aucune assistante maternelle disponible',
|
||||||
|
noResultsMessage: 'Aucune AM avec place libre pour cette recherche',
|
||||||
|
toggleFilter: const AdminSelectToggleFilter<AssistanteMaternelleModel>(
|
||||||
|
label: 'Libre',
|
||||||
|
initialValue: true,
|
||||||
|
whenEnabled: amHasFreePlace,
|
||||||
|
),
|
||||||
|
loadItems: () async {
|
||||||
|
final list = await UserService.getAssistantesMaternelles();
|
||||||
|
return list
|
||||||
|
.where((am) => !excludeIds.contains(am.user.id))
|
||||||
|
.toList()
|
||||||
|
..sort(
|
||||||
|
(a, b) => a.user.fullName
|
||||||
|
.toLowerCase()
|
||||||
|
.compareTo(b.user.fullName.toLowerCase()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
matchesQuery: (am, q) {
|
||||||
|
final u = am.user;
|
||||||
|
final name = u.fullName.toLowerCase();
|
||||||
|
final fn = (u.prenom ?? '').toLowerCase();
|
||||||
|
final ln = (u.nom ?? '').toLowerCase();
|
||||||
|
final zone = (am.residenceCity ?? '').toLowerCase();
|
||||||
|
final agrement = (am.approvalNumber ?? '').toLowerCase();
|
||||||
|
return name.contains(q) ||
|
||||||
|
fn.contains(q) ||
|
||||||
|
ln.contains(q) ||
|
||||||
|
zone.contains(q) ||
|
||||||
|
agrement.contains(q);
|
||||||
|
},
|
||||||
|
resolveSelect: (ctx, am, reload) =>
|
||||||
|
_resolveAmSelection(ctx, am, reload),
|
||||||
|
itemBuilder: (context, am, onSelect) {
|
||||||
|
final full = !amHasFreePlace(am);
|
||||||
|
return AdminUserCard(
|
||||||
|
title: am.user.fullName,
|
||||||
|
avatarUrl: am.user.photoUrl,
|
||||||
|
fallbackIcon: Icons.face,
|
||||||
|
subtitleLines: _amSelectSubtitleLines(am),
|
||||||
|
vigilanceTooltip: amPlacesVigilanceMessage(am),
|
||||||
|
onCardTap: onSelect,
|
||||||
|
margin: const EdgeInsets.only(bottom: 4),
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 5,
|
||||||
|
),
|
||||||
|
backgroundColor: full ? const Color(0xFFFFEBEE) : null,
|
||||||
|
borderColor: full ? Colors.red.shade200 : null,
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.add_link),
|
||||||
|
tooltip: 'Rattacher',
|
||||||
|
onPressed: onSelect,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<AssistanteMaternelleModel?> _resolveAmSelection(
|
||||||
|
BuildContext context,
|
||||||
|
AssistanteMaternelleModel am,
|
||||||
|
Future<void> Function() reloadList,
|
||||||
|
) async {
|
||||||
|
if (amHasFreePlace(am)) return am;
|
||||||
|
|
||||||
|
final selected = await showDialog<AssistanteMaternelleModel>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => _AmNoPlaceWarningDialog(initialAm: am),
|
||||||
|
);
|
||||||
|
await reloadList();
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Avertissement AM saturée + lien vers la fiche pour ajuster les places.
|
||||||
|
class _AmNoPlaceWarningDialog extends StatefulWidget {
|
||||||
|
final AssistanteMaternelleModel initialAm;
|
||||||
|
|
||||||
|
const _AmNoPlaceWarningDialog({required this.initialAm});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_AmNoPlaceWarningDialog> createState() =>
|
||||||
|
_AmNoPlaceWarningDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AmNoPlaceWarningDialogState extends State<_AmNoPlaceWarningDialog> {
|
||||||
|
late AssistanteMaternelleModel _am;
|
||||||
|
TapGestureRecognizer? _linkRecognizer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_am = widget.initialAm;
|
||||||
|
_linkRecognizer = TapGestureRecognizer()..onTap = _openAmFiche;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_linkRecognizer?.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _openAmFiche() async {
|
||||||
|
if (!mounted) return;
|
||||||
|
await showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AdminAmEditModal(
|
||||||
|
assistante: _am,
|
||||||
|
onSaved: () async {
|
||||||
|
try {
|
||||||
|
final fresh =
|
||||||
|
await UserService.getAssistanteMaternelle(_am.user.id);
|
||||||
|
if (mounted) setState(() => _am = fresh);
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final fresh = await UserService.getAssistanteMaternelle(_am.user.id);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _am = fresh);
|
||||||
|
if (amHasFreePlace(fresh)) {
|
||||||
|
Navigator.of(context).pop(fresh);
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final name = _am.user.fullName.trim().isNotEmpty
|
||||||
|
? _am.user.fullName.trim()
|
||||||
|
: 'Cette assistante maternelle';
|
||||||
|
const linkColor = ValidationModalTheme.primaryActionBackground;
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Plus de place disponible'),
|
||||||
|
content: Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
style: const TextStyle(fontSize: 14, color: Colors.black87, height: 1.4),
|
||||||
|
children: [
|
||||||
|
TextSpan(
|
||||||
|
text: '$name n\'a plus de place libre pour accueillir '
|
||||||
|
'un enfant supplémentaire.\n\n',
|
||||||
|
),
|
||||||
|
const TextSpan(text: 'Vous pouvez '),
|
||||||
|
TextSpan(
|
||||||
|
text: 'ouvrir sa fiche',
|
||||||
|
style: TextStyle(
|
||||||
|
color: linkColor,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
recognizer: _linkRecognizer,
|
||||||
|
),
|
||||||
|
const TextSpan(
|
||||||
|
text: ' pour modifier la capacité ou les places, '
|
||||||
|
'puis la sélectionner si une place se libère.',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Fermer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,7 +18,7 @@ class AdminSelectToggleFilter<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Shell générique « rechercher + liste + sélection » pour les modales admin.
|
/// Shell générique « rechercher + liste + sélection » pour les modales admin.
|
||||||
/// Utilisé par la sélection d'enfant (#146) et prévu pour la sélection d'AM (#147).
|
/// Utilisé par la sélection d'enfant (#146) et la sélection d'AM (#147).
|
||||||
class AdminSelectListModal<T> extends StatefulWidget {
|
class AdminSelectListModal<T> extends StatefulWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final String searchHint;
|
final String searchHint;
|
||||||
@ -39,9 +39,17 @@ class AdminSelectListModal<T> extends StatefulWidget {
|
|||||||
/// Nombre minimum de cartes visibles dans la zone scrollable.
|
/// Nombre minimum de cartes visibles dans la zone scrollable.
|
||||||
final int minVisibleCards;
|
final int minVisibleCards;
|
||||||
|
|
||||||
/// Switch optionnel à droite du champ de recherche (ex. « Sans garde »).
|
/// Switch optionnel à droite du champ de recherche (ex. « Sans garde », « Libre »).
|
||||||
final AdminSelectToggleFilter<T>? toggleFilter;
|
final AdminSelectToggleFilter<T>? toggleFilter;
|
||||||
|
|
||||||
|
/// Si fourni, appelé avant de valider la sélection.
|
||||||
|
/// Retourne l'élément à pop (éventuellement rafraîchi), ou `null` pour annuler.
|
||||||
|
final Future<T?> Function(
|
||||||
|
BuildContext context,
|
||||||
|
T item,
|
||||||
|
Future<void> Function() reload,
|
||||||
|
)? resolveSelect;
|
||||||
|
|
||||||
const AdminSelectListModal({
|
const AdminSelectListModal({
|
||||||
super.key,
|
super.key,
|
||||||
required this.title,
|
required this.title,
|
||||||
@ -55,6 +63,7 @@ class AdminSelectListModal<T> extends StatefulWidget {
|
|||||||
this.cardExtent = 52,
|
this.cardExtent = 52,
|
||||||
this.minVisibleCards = 8,
|
this.minVisibleCards = 8,
|
||||||
this.toggleFilter,
|
this.toggleFilter,
|
||||||
|
this.resolveSelect,
|
||||||
});
|
});
|
||||||
|
|
||||||
static Future<T?> show<T>(
|
static Future<T?> show<T>(
|
||||||
@ -74,6 +83,11 @@ class AdminSelectListModal<T> extends StatefulWidget {
|
|||||||
double cardExtent = 52,
|
double cardExtent = 52,
|
||||||
int minVisibleCards = 8,
|
int minVisibleCards = 8,
|
||||||
AdminSelectToggleFilter<T>? toggleFilter,
|
AdminSelectToggleFilter<T>? toggleFilter,
|
||||||
|
Future<T?> Function(
|
||||||
|
BuildContext context,
|
||||||
|
T item,
|
||||||
|
Future<void> Function() reload,
|
||||||
|
)? resolveSelect,
|
||||||
}) {
|
}) {
|
||||||
return showDialog<T>(
|
return showDialog<T>(
|
||||||
context: context,
|
context: context,
|
||||||
@ -89,6 +103,7 @@ class AdminSelectListModal<T> extends StatefulWidget {
|
|||||||
cardExtent: cardExtent,
|
cardExtent: cardExtent,
|
||||||
minVisibleCards: minVisibleCards,
|
minVisibleCards: minVisibleCards,
|
||||||
toggleFilter: toggleFilter,
|
toggleFilter: toggleFilter,
|
||||||
|
resolveSelect: resolveSelect,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -104,6 +119,7 @@ class _AdminSelectListModalState<T> extends State<AdminSelectListModal<T>> {
|
|||||||
bool _loading = true;
|
bool _loading = true;
|
||||||
String? _error;
|
String? _error;
|
||||||
late bool _toggleOn;
|
late bool _toggleOn;
|
||||||
|
bool _resolving = false;
|
||||||
|
|
||||||
double get _listHeight =>
|
double get _listHeight =>
|
||||||
widget.cardExtent * widget.minVisibleCards;
|
widget.cardExtent * widget.minVisibleCards;
|
||||||
@ -143,6 +159,23 @@ class _AdminSelectListModalState<T> extends State<AdminSelectListModal<T>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _handleSelect(T item) async {
|
||||||
|
if (_resolving) return;
|
||||||
|
final resolve = widget.resolveSelect;
|
||||||
|
if (resolve == null) {
|
||||||
|
Navigator.of(context).pop(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() => _resolving = true);
|
||||||
|
try {
|
||||||
|
final chosen = await resolve(context, item, _load);
|
||||||
|
if (!mounted || chosen == null) return;
|
||||||
|
Navigator.of(context).pop(chosen);
|
||||||
|
} finally {
|
||||||
|
if (mounted) setState(() => _resolving = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
List<T> get _filtered {
|
List<T> get _filtered {
|
||||||
var list = _all;
|
var list = _all;
|
||||||
final toggle = widget.toggleFilter;
|
final toggle = widget.toggleFilter;
|
||||||
@ -326,7 +359,7 @@ class _AdminSelectListModalState<T> extends State<AdminSelectListModal<T>> {
|
|||||||
return widget.itemBuilder(
|
return widget.itemBuilder(
|
||||||
context,
|
context,
|
||||||
item,
|
item,
|
||||||
() => Navigator.of(context).pop(item),
|
() => _handleSelect(item),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user