timetable performance optimization

This commit is contained in:
2026-09-25 22:27:10 +02:00
parent ef7d17033d
commit ed8e52f475
13 changed files with 391 additions and 137 deletions
@@ -1,16 +1,14 @@
part of '../custom_workweek_calendar.dart';
class _OutsideHoursStrip extends StatelessWidget {
final DateTime weekStart;
final List<Appointment> appointments;
final List<List<Appointment>> outside;
final double rulerWidth;
final void Function(Appointment) onAppointmentTap;
final bool Function(Appointment) isCrossedOut;
const _OutsideHoursStrip({
super.key,
required this.weekStart,
required this.appointments,
required this.outside,
required this.rulerWidth,
required this.onAppointmentTap,
required this.isCrossedOut,
@@ -18,10 +16,6 @@ class _OutsideHoursStrip extends StatelessWidget {
@override
Widget build(BuildContext context) {
final outside = partitionAppointmentsForWeek(
appointments,
weekStart,
).outside;
if (outside.every((day) => day.isEmpty)) return const SizedBox.shrink();
final theme = Theme.of(context);
@@ -1,10 +1,31 @@
part of '../custom_workweek_calendar.dart';
/// Per-week derived data, computed once per appointment/region set instead of
/// on every rebuild of a week page.
class _WeekData {
final List<List<Appointment>> inside;
final List<List<Appointment>> outside;
final List<List<BoundRegion>> regions;
_WeekData._(this.inside, this.outside, this.regions);
factory _WeekData.compute(
DateTime weekStart,
List<Appointment> appointments,
List<TimeRegion> timeRegions,
) {
final partitioned = partitionAppointmentsForWeek(appointments, weekStart);
return _WeekData._(partitioned.inside, partitioned.outside, [
for (var d = 0; d < 5; d++)
expandRegionsForDay(timeRegions, weekStart.addDays(d)),
]);
}
}
class _WeekGrid extends StatelessWidget {
final DateTime weekStart;
final LessonPeriodSchedule schedule;
final List<Appointment> appointments;
final List<TimeRegion> timeRegions;
final _WeekData data;
final void Function(Appointment) onAppointmentTap;
final bool Function(Appointment) isCrossedOut;
final void Function(DateTime start, DateTime end)? onCreateEvent;
@@ -16,8 +37,7 @@ class _WeekGrid extends StatelessWidget {
const _WeekGrid({
required this.weekStart,
required this.schedule,
required this.appointments,
required this.timeRegions,
required this.data,
required this.onAppointmentTap,
required this.isCrossedOut,
required this.onCreateEvent,
@@ -29,8 +49,6 @@ class _WeekGrid extends StatelessWidget {
@override
Widget build(BuildContext context) {
final partitioned = partitionAppointmentsForWeek(appointments, weekStart);
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@@ -48,8 +66,8 @@ class _WeekGrid extends StatelessWidget {
child: _DayColumn(
date: weekStart.addDays(d),
schedule: schedule,
appointments: partitioned.inside[d],
timeRegions: timeRegions,
appointments: data.inside[d],
regions: data.regions[d],
layout: layout,
today: today,
nowNotifier: nowNotifier,
@@ -195,7 +213,7 @@ class _DayColumn extends StatelessWidget {
final DateTime date;
final LessonPeriodSchedule schedule;
final List<Appointment> appointments;
final List<TimeRegion> timeRegions;
final List<BoundRegion> regions;
final PeriodLayout layout;
final DateTime today;
final ValueListenable<DateTime> nowNotifier;
@@ -207,7 +225,7 @@ class _DayColumn extends StatelessWidget {
required this.date,
required this.schedule,
required this.appointments,
required this.timeRegions,
required this.regions,
required this.layout,
required this.today,
required this.nowNotifier,
@@ -280,12 +298,10 @@ class _DayColumn extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Theme.of(context);
final dayAppointments = appointments;
final dayRegions = expandRegionsForDay(timeRegions, date);
final isToday = date.isSameDay(today);
final isTablet = MediaQuery.of(context).size.shortestSide >= 600;
final laidOut = assignLanes(dayAppointments, maxLanes: isTablet ? 3 : 2);
final isTablet = MediaQuery.sizeOf(context).shortestSide >= 600;
final laidOut = assignLanes(appointments, maxLanes: isTablet ? 3 : 2);
final dayName = DateFormat(
'EEEE',
@@ -304,8 +320,7 @@ class _DayColumn extends StatelessWidget {
sortKey: OrdinalSortKey(date.weekday.toDouble()),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onLongPressStart: (details) =>
_handleLongPress(details, dayAppointments),
onLongPressStart: (details) => _handleLongPress(details, appointments),
child: DecoratedBox(
decoration: BoxDecoration(
color: isToday ? theme.colorScheme.primary.withAlpha(14) : null,
@@ -342,7 +357,7 @@ class _DayColumn extends StatelessWidget {
color: theme.dividerColor.withAlpha(60),
),
),
for (final region in dayRegions)
for (final region in regions)
Positioned(
top: layout.yOfDateTime(region.start),
height:
@@ -379,6 +394,7 @@ class _DayColumn extends StatelessWidget {
onTap: () => onAppointmentTap(appointment),
child: AppointmentTile(
appointment: appointment,
now: nowNotifier,
crossedOut: isCrossedOut(appointment),
),
),