bugfix: resolve runtime issues - encoding accents, ListView dynamic heights, notifyListeners exceptions during build phase, and layout overflows

This commit is contained in:
ElPoyo
2026-05-26 14:48:39 +02:00
parent 9bc4e88e46
commit f8f6cfb102
6 changed files with 399 additions and 397 deletions
+3 -3
View File
@@ -80,7 +80,7 @@ class EquipmentProvider extends ChangeNotifier {
Future<void> loadEquipments() async {
print('[EquipmentProvider] Starting to load ALL equipments...');
_isLoading = true;
notifyListeners();
scheduleMicrotask(notifyListeners);
try {
_equipment.clear();
@@ -272,7 +272,7 @@ class EquipmentProvider extends ChangeNotifier {
_lastVisible = null;
_hasMore = true;
_isLoading = true;
notifyListeners();
scheduleMicrotask(notifyListeners);
try {
await loadNextPage();
@@ -296,7 +296,7 @@ class EquipmentProvider extends ChangeNotifier {
_isLoadingMore = true;
_isLoading = true;
notifyListeners();
scheduleMicrotask(notifyListeners);
try {
final result = await _dataService.getEquipmentsPaginated(
+1 -1
View File
@@ -28,7 +28,7 @@ class AppInitializer with ChangeNotifier {
Future<void> initialize() async {
if (_isInitialized || _isInitializing) return;
_isInitializing = true;
notifyListeners();
scheduleMicrotask(() => notifyListeners());
try {
// Initialiser Firebase
+228 -222
View File
@@ -980,237 +980,243 @@ class _CalendarPageState extends State<CalendarPage> {
? eventsForSelectedDay[_selectedEventIndex]
: null;
// GESTURE DETECTOR pour swipe vertical (plier/déplier) et horizontal (mois)
return GestureDetector(
onVerticalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe vers le haut : plier
setState(() {
_calendarCollapsed = true;
});
} else if (details.primaryVelocity! > 200) {
// Swipe vers le bas : déplier
setState(() {
_calendarCollapsed = false;
});
}
}
},
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : mois suivant
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
},
child: Stack(
children: [
// Calendrier + détails en dessous
AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
top: _calendarCollapsed ? -600 : 0, // cache le calendrier en haut
left: 0,
right: 0,
height: _calendarCollapsed ? 0 : null,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
_buildMonthHeader(context),
if (!_calendarCollapsed)
// Ajout d'un GestureDetector pour swipe horizontal sur le calendrier
GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : mois suivant
final newMonth = DateTime(
_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
final newMonth = DateTime(
_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
},
child: MobileCalendarView(
focusedDay: _focusedDay,
selectedDay: _selectedDay,
events: filteredEvents,
onDaySelected: (day) {
final eventsForDay = filteredEvents
.where((e) =>
e.startDateTime.year == day.year &&
e.startDateTime.month == day.month &&
e.startDateTime.day == day.day)
.toList()
..sort((a, b) =>
a.startDateTime.compareTo(b.startDateTime));
setState(() {
_selectedDay = day;
_calendarCollapsed = false;
_selectedEventIndex = 0;
_selectedEvent = eventsForDay.isNotEmpty
? eventsForDay[0]
: null;
});
},
),
),
Expanded(
child: hasEvents
// Ajout d'un GestureDetector pour swipe horizontal sur le détail événement
? GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : événement suivant
if (_selectedEventIndex <
eventsForSelectedDay.length - 1) {
setState(() {
_selectedEventIndex++;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
} else if (details.primaryVelocity! > 200) {
// Swipe droite : événement précédent
if (_selectedEventIndex > 0) {
setState(() {
_selectedEventIndex--;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
}
}
},
child: EventDetails(
event: eventsForSelectedDay[_selectedEventIndex],
selectedDate: _selectedDay,
events: eventsForSelectedDay,
onSelectEvent: (event, date) {
final idx = eventsForSelectedDay
.indexWhere((e) => e.id == event.id);
return LayoutBuilder(
builder: (context, constraints) {
final maxHeight = constraints.maxHeight;
// GESTURE DETECTOR pour swipe vertical (plier/déplier) et horizontal (mois)
return GestureDetector(
onVerticalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe vers le haut : plier
setState(() {
_calendarCollapsed = true;
});
} else if (details.primaryVelocity! > 200) {
// Swipe vers le bas : déplier
setState(() {
_calendarCollapsed = false;
});
}
}
},
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : mois suivant
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
final newMonth =
DateTime(_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
},
child: Stack(
children: [
// Calendrier + détails en dessous
AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
top: _calendarCollapsed ? -maxHeight : 0, // cache le calendrier en haut
left: 0,
right: 0,
height: _calendarCollapsed ? 0 : null,
child: SizedBox(
height: maxHeight,
child: Column(
children: [
_buildMonthHeader(context),
if (!_calendarCollapsed)
// Ajout d'un GestureDetector pour swipe horizontal sur le calendrier
GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : mois suivant
final newMonth = DateTime(
_focusedDay.year, _focusedDay.month + 1, 1);
setState(() {
_selectedEventIndex = idx >= 0 ? idx : 0;
_selectedEvent = event;
_focusedDay = newMonth;
});
},
),
)
: Center(
child: Text(
'Aucun événement ne démarre à cette date')),
),
],
),
),
),
// Vue détail (prend tout l'espace quand calendrier cache)
if (_calendarCollapsed && _selectedDay != null)
AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
top: _calendarCollapsed ? 0 : 600,
left: 0,
right: 0,
bottom: 0,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
_buildMonthHeader(context),
Expanded(
child: Stack(
children: [
if (currentEvent != null)
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
} else if (details.primaryVelocity! > 200) {
// Swipe droite : mois précédent
final newMonth = DateTime(
_focusedDay.year, _focusedDay.month - 1, 1);
setState(() {
_focusedDay = newMonth;
});
print(
'[CalendarPage] Month changed to ${newMonth.year}-${newMonth.month}');
_loadCurrentMonthEvents();
}
}
},
child: MobileCalendarView(
focusedDay: _focusedDay,
selectedDay: _selectedDay,
events: filteredEvents,
onDaySelected: (day) {
final eventsForDay = filteredEvents
.where((e) =>
e.startDateTime.year == day.year &&
e.startDateTime.month == day.month &&
e.startDateTime.day == day.day)
.toList()
..sort((a, b) =>
a.startDateTime.compareTo(b.startDateTime));
setState(() {
_selectedDay = day;
_calendarCollapsed = false;
_selectedEventIndex = 0;
_selectedEvent = eventsForDay.isNotEmpty
? eventsForDay[0]
: null;
});
},
),
),
Expanded(
child: hasEvents
// Ajout d'un GestureDetector pour swipe horizontal sur le détail événement
GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : événement suivant
if (_selectedEventIndex <
eventsForSelectedDay.length - 1) {
setState(() {
_selectedEventIndex++;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
} else if (details.primaryVelocity! > 200) {
// Swipe droite : événement précédent
if (_selectedEventIndex > 0) {
setState(() {
_selectedEventIndex--;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
? GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : événement suivant
if (_selectedEventIndex <
eventsForSelectedDay.length - 1) {
setState(() {
_selectedEventIndex++;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
} else if (details.primaryVelocity! > 200) {
// Swipe droite : événement précédent
if (_selectedEventIndex > 0) {
setState(() {
_selectedEventIndex--;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
}
}
}
},
child: EventDetails(
event: currentEvent,
selectedDate: _selectedDay,
events: eventsForSelectedDay,
onSelectEvent: (event, date) {
final idx = eventsForSelectedDay
.indexWhere((e) => e.id == event.id);
setState(() {
_selectedEventIndex = idx >= 0 ? idx : 0;
_selectedEvent = event;
});
},
),
),
if (!hasEvents)
const Center(
child: Text(
'Aucun événement ne démarre à cette date'),
),
],
child: EventDetails(
event: eventsForSelectedDay[_selectedEventIndex],
selectedDate: _selectedDay,
events: eventsForSelectedDay,
onSelectEvent: (event, date) {
final idx = eventsForSelectedDay
.indexWhere((e) => e.id == event.id);
setState(() {
_selectedEventIndex = idx >= 0 ? idx : 0;
_selectedEvent = event;
});
},
),
)
: Center(
child: Text(
'Aucun événement ne démarre à cette date')),
),
),
],
],
),
),
),
),
],
),
// Vue détail (prend tout l'espace quand calendrier cache)
if (_calendarCollapsed && _selectedDay != null)
AnimatedPositioned(
duration: const Duration(milliseconds: 400),
curve: Curves.easeInOut,
top: _calendarCollapsed ? 0 : maxHeight,
left: 0,
right: 0,
bottom: 0,
child: SizedBox(
height: maxHeight,
child: Column(
children: [
_buildMonthHeader(context),
Expanded(
child: Stack(
children: [
if (currentEvent != null)
// Ajout d'un GestureDetector pour swipe horizontal sur le détail événement
GestureDetector(
onHorizontalDragEnd: (details) {
if (details.primaryVelocity != null) {
if (details.primaryVelocity! < -200) {
// Swipe gauche : événement suivant
if (_selectedEventIndex <
eventsForSelectedDay.length - 1) {
setState(() {
_selectedEventIndex++;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
} else if (details.primaryVelocity! > 200) {
// Swipe droite : événement précédent
if (_selectedEventIndex > 0) {
setState(() {
_selectedEventIndex--;
_selectedEvent = eventsForSelectedDay[
_selectedEventIndex];
});
}
}
}
},
child: EventDetails(
event: currentEvent,
selectedDate: _selectedDay,
events: eventsForSelectedDay,
onSelectEvent: (event, date) {
final idx = eventsForSelectedDay
.indexWhere((e) => e.id == event.id);
setState(() {
_selectedEventIndex = idx >= 0 ? idx : 0;
_selectedEvent = event;
});
},
),
),
if (!hasEvents)
const Center(
child: Text(
'Aucun événement ne démarre à cette date'),
),
],
),
),
],
),
),
),
],
),
);
},
);
}
+6 -8
View File
@@ -57,6 +57,12 @@ class _EquipmentFormPageState extends State<EquipmentFormPage> {
final provider = Provider.of<EquipmentProvider>(context, listen: false);
provider.loadBrands();
provider.loadModels();
if (widget.equipment != null) {
if (_selectedBrand != null && _selectedBrand!.isNotEmpty) {
_loadFilteredModels(_selectedBrand!);
}
_loadFilteredSubCategories(_selectedCategory);
}
});
if (widget.equipment != null) {
_populateFields();
@@ -84,14 +90,6 @@ class _EquipmentFormPageState extends State<EquipmentFormPage> {
});
DebugLog.info('[EquipmentForm] Populating fields for equipment: ${equipment.id}');
if (_selectedBrand != null && _selectedBrand!.isNotEmpty) {
_loadFilteredModels(_selectedBrand!);
}
// Charger les sous-catégories pour la catégorie sélectionnée
_loadFilteredSubCategories(_selectedCategory);
}
@@ -500,12 +500,7 @@ class _EquipmentManagementPageState extends State<EquipmentManagementPage>
return ListView.builder(
controller: _scrollController,
itemCount: itemCount,
// ✅ prototypeItem utilisé car les cartes ont des hauteurs variables :
// - Les équipements standards (ListTile + margin) font ~88px
// - Les consommables/câbles affichent _buildQuantityDisplay en plus (~30px)
// - prototypeItem permet à Flutter d'optimiser le scroll sans couper les items
prototypeItem: const SizedBox(height: 88),
// ✅ Augmenter le cache pour un scroll plus fluide
// ✅ Augmenter le cache pour un scroll plus fluide (prototypeItem retiré car les hauteurs dynamiques varient selon le type d'équipement)
cacheExtent: 500, // Précharger 500px en plus
itemBuilder: (context, index) {
// Dernier élément = indicateur de chargement
File diff suppressed because it is too large Load Diff