import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; class CalendarPage extends StatefulWidget { const CalendarPage({super.key}); @override State createState() => _CalendarPageState(); } class _CalendarPageState extends State { late DateTime _focusedMonth; DateTime? _selectedDate; @override void initState() { super.initState(); _focusedMonth = DateTime.now(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; final locale = Localizations.localeOf(context).languageCode; return Scaffold( appBar: AppBar( title: Text(l10n.calendar), leading: IconButton( icon: const Icon(Icons.close), onPressed: () => context.pop(), ), ), body: Column( children: [ _MonthNavigation( month: _focusedMonth, locale: locale, onPrevious: () { setState(() { _focusedMonth = DateTime(_focusedMonth.year, _focusedMonth.month - 1); }); }, onNext: () { setState(() { _focusedMonth = DateTime(_focusedMonth.year, _focusedMonth.month + 1); }); }, ), _WeekdayHeaders(locale: locale), Expanded( child: _CalendarGrid( focusedMonth: _focusedMonth, selectedDate: _selectedDate, onDateSelected: (date) { setState(() => _selectedDate = date); }, ), ), ], ), bottomNavigationBar: _selectedDate != null ? SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: ElevatedButton( onPressed: () { final dateStr = DateFormat('yyyy-MM-dd').format(_selectedDate!); context.go('/?date=$dateStr'); }, child: Text(l10n.goToDay), ), ), ) : null, ); } } class _MonthNavigation extends StatelessWidget { final DateTime month; final String locale; final VoidCallback onPrevious; final VoidCallback onNext; const _MonthNavigation({ required this.month, required this.locale, required this.onPrevious, required this.onNext, }); @override Widget build(BuildContext context) { final monthFormat = DateFormat.yMMMM(locale); return Padding( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( icon: const Icon(Icons.chevron_left), onPressed: onPrevious, ), Text( monthFormat.format(month), style: Theme.of(context).textTheme.titleLarge, ), IconButton( icon: const Icon(Icons.chevron_right), onPressed: onNext, ), ], ), ); } } class _WeekdayHeaders extends StatelessWidget { final String locale; const _WeekdayHeaders({required this.locale}); @override Widget build(BuildContext context) { final weekdays = DateFormat.E(locale); final today = DateTime.now(); final monday = today.subtract(Duration(days: today.weekday - 1)); return Padding( padding: const EdgeInsets.symmetric(horizontal: 8), child: Row( children: List.generate(7, (index) { final day = monday.add(Duration(days: index)); return Expanded( child: Center( child: Text( weekdays.format(day).substring(0, 2), style: Theme.of(context).textTheme.bodySmall?.copyWith( fontWeight: FontWeight.bold, ), ), ), ); }), ), ); } } class _CalendarGrid extends StatelessWidget { final DateTime focusedMonth; final DateTime? selectedDate; final ValueChanged onDateSelected; const _CalendarGrid({ required this.focusedMonth, required this.selectedDate, required this.onDateSelected, }); @override Widget build(BuildContext context) { final firstDayOfMonth = DateTime(focusedMonth.year, focusedMonth.month, 1); final lastDayOfMonth = DateTime(focusedMonth.year, focusedMonth.month + 1, 0); final startOffset = (firstDayOfMonth.weekday - 1) % 7; final totalDays = startOffset + lastDayOfMonth.day; final rowCount = (totalDays / 7).ceil(); final today = DateTime.now(); return GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 7, childAspectRatio: 1, ), itemCount: rowCount * 7, itemBuilder: (context, index) { final dayOffset = index - startOffset; if (dayOffset < 0 || dayOffset >= lastDayOfMonth.day) { return const SizedBox(); } final date = DateTime(focusedMonth.year, focusedMonth.month, dayOffset + 1); final isToday = date.year == today.year && date.month == today.month && date.day == today.day; final isSelected = selectedDate != null && date.year == selectedDate!.year && date.month == selectedDate!.month && date.day == selectedDate!.day; return GestureDetector( onTap: () => onDateSelected(date), child: Container( margin: const EdgeInsets.all(2), decoration: BoxDecoration( color: isSelected ? Theme.of(context).colorScheme.primary : isToday ? Theme.of(context).colorScheme.primaryContainer : null, borderRadius: BorderRadius.circular(8), ), child: Center( child: Text( '${dayOffset + 1}', style: TextStyle( color: isSelected ? Theme.of(context).colorScheme.onPrimary : isToday ? Theme.of(context).colorScheme.onPrimaryContainer : null, fontWeight: isToday || isSelected ? FontWeight.bold : null, ), ), ), ), ); }, ); } }