174 lines
5.7 KiB
Dart
174 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:table_calendar/table_calendar.dart';
|
|
|
|
import '../../data/models/calendar_event.dart';
|
|
import '../../providers/calendar_provider.dart';
|
|
import 'event_form_sheet.dart';
|
|
|
|
class CalendarScreen extends ConsumerWidget {
|
|
const CalendarScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final calAsync = ref.watch(calendarProvider);
|
|
final notifier = ref.read(calendarProvider.notifier);
|
|
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('Calendar')),
|
|
body: calAsync.when(
|
|
loading: () => const Center(child: CircularProgressIndicator()),
|
|
error: (e, _) => Center(
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
const Text('Could not load calendar.'),
|
|
const SizedBox(height: 12),
|
|
FilledButton.tonal(
|
|
onPressed: () => ref.invalidate(calendarProvider),
|
|
child: const Text('Retry'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
data: (cal) => Column(
|
|
children: [
|
|
TableCalendar<CalendarEvent>(
|
|
firstDay: DateTime(2020),
|
|
lastDay: DateTime(2030),
|
|
focusedDay: cal.focusedMonth,
|
|
selectedDayPredicate: (day) => isSameDay(day, cal.selectedDay),
|
|
eventLoader: (day) =>
|
|
cal.eventsByDay[dateOnly(day)] ?? [],
|
|
calendarFormat: CalendarFormat.month,
|
|
headerStyle: const HeaderStyle(
|
|
formatButtonVisible: false,
|
|
titleCentered: true,
|
|
),
|
|
calendarStyle: CalendarStyle(
|
|
selectedDecoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.primary,
|
|
shape: BoxShape.circle,
|
|
),
|
|
todayDecoration: BoxDecoration(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary
|
|
.withValues(alpha: 0.3),
|
|
shape: BoxShape.circle,
|
|
),
|
|
markerDecoration: BoxDecoration(
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
onDaySelected: (selected, _) => notifier.selectDay(selected),
|
|
onPageChanged: (focused) => notifier.loadMonth(focused),
|
|
),
|
|
const Divider(height: 1),
|
|
Expanded(
|
|
child: RefreshIndicator(
|
|
onRefresh: () => ref.read(calendarProvider.notifier).refresh(),
|
|
child: _AgendaList(
|
|
events:
|
|
cal.eventsByDay[dateOnly(cal.selectedDay)] ?? [],
|
|
notifier: notifier,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
floatingActionButton: calAsync.hasValue
|
|
? FloatingActionButton(
|
|
onPressed: () => showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
useSafeArea: true,
|
|
builder: (_) => EventFormSheet(
|
|
event: null,
|
|
initialDate: calAsync.value!.selectedDay,
|
|
notifier: notifier,
|
|
),
|
|
),
|
|
child: const Icon(Icons.add),
|
|
)
|
|
: null,
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Agenda list ───────────────────────────────────────────────────────────────
|
|
|
|
class _AgendaList extends StatelessWidget {
|
|
final List<CalendarEvent> events;
|
|
final CalendarNotifier notifier;
|
|
|
|
const _AgendaList({required this.events, required this.notifier});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (events.isEmpty) {
|
|
return ListView(
|
|
children: const [
|
|
SizedBox(height: 80),
|
|
Center(child: Text('No events')),
|
|
],
|
|
);
|
|
}
|
|
return ListView.builder(
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
|
itemCount: events.length,
|
|
itemBuilder: (_, i) =>
|
|
_EventTile(event: events[i], notifier: notifier),
|
|
);
|
|
}
|
|
}
|
|
|
|
// ── Event tile ────────────────────────────────────────────────────────────────
|
|
|
|
class _EventTile extends StatelessWidget {
|
|
final CalendarEvent event;
|
|
final CalendarNotifier notifier;
|
|
|
|
const _EventTile({required this.event, required this.notifier});
|
|
|
|
Color _dotColor(BuildContext context) {
|
|
if (event.color.isEmpty) return Theme.of(context).colorScheme.primary;
|
|
try {
|
|
return Color(int.parse(event.color.replaceFirst('#', '0xFF')));
|
|
} catch (_) {
|
|
return Theme.of(context).colorScheme.primary;
|
|
}
|
|
}
|
|
|
|
String _timeLabel() {
|
|
if (event.allDay) return 'All day';
|
|
final h = event.startDt.hour.toString().padLeft(2, '0');
|
|
final m = event.startDt.minute.toString().padLeft(2, '0');
|
|
return '$h:$m';
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListTile(
|
|
leading: CircleAvatar(
|
|
radius: 6,
|
|
backgroundColor: _dotColor(context),
|
|
),
|
|
title: Text(event.title),
|
|
subtitle: Text(_timeLabel()),
|
|
onTap: () => showModalBottomSheet(
|
|
context: context,
|
|
isScrollControlled: true,
|
|
useSafeArea: true,
|
|
builder: (_) => EventFormSheet(
|
|
event: event,
|
|
initialDate: null,
|
|
notifier: notifier,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|