Files
bini-google-tv/flutter_app/lib/widgets/calendar_widget.dart

184 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../models/schedule_item.dart';
import '../services/schedule_service.dart';
class CalendarWidget extends StatefulWidget {
const CalendarWidget({super.key});
@override
State<CalendarWidget> createState() => _CalendarWidgetState();
}
class _CalendarWidgetState extends State<CalendarWidget> {
List<ScheduleItem> _schedules = [];
@override
void initState() {
super.initState();
_loadSchedules();
}
Future<void> _loadSchedules() async {
try {
final schedules =
await Provider.of<ScheduleService>(context, listen: false)
.fetchMonthlySchedules();
if (mounted) {
setState(() {
_schedules = schedules;
});
}
} catch (e) {
debugPrint('Error loading schedules: $e');
}
}
bool _hasScheduleOn(DateTime date) {
final checkDate = DateTime(date.year, date.month, date.day);
return _schedules.any((s) {
final start =
DateTime(s.startDate.year, s.startDate.month, s.startDate.day);
final end = DateTime(s.endDate.year, s.endDate.month, s.endDate.day);
return (checkDate.isAtSameMomentAs(start) || checkDate.isAfter(start)) &&
(checkDate.isAtSameMomentAs(end) || checkDate.isBefore(end));
});
}
@override
Widget build(BuildContext context) {
final now = DateTime.now();
final firstDayOfMonth = DateTime(now.year, now.month, 1);
final lastDayOfMonth = DateTime(now.year, now.month + 1, 0);
final daysInMonth = lastDayOfMonth.day;
final startingWeekday = firstDayOfMonth.weekday; // Mon=1, Sun=7
int offset = startingWeekday % 7;
return Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).cardTheme.color,
borderRadius: BorderRadius.circular(16),
),
child: Column(
children: [
// Header
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateFormat('yyyy년 M월').format(now),
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
color: Colors.white,
fontSize: 20,
),
),
const Icon(Icons.calendar_today,
color: Colors.white54, size: 20),
],
),
),
const SizedBox(height: 8),
// Days Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: ['', '', '', '', '', '', ''].asMap().entries.map((entry) {
final isSunday = entry.key == 0;
return Expanded(
child: Center(
child: Text(
entry.value,
style: TextStyle(
color: isSunday ? Colors.redAccent : Colors.white54,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
),
);
}).toList(),
),
const SizedBox(height: 4),
// Days Grid
Expanded(
child: Column(
children: List.generate(6, (row) {
return Expanded(
child: Row(
children: List.generate(7, (col) {
final index = row * 7 + col;
final dayNumber = index - offset + 1;
final isSunday = col == 0;
if (dayNumber < 1 || dayNumber > daysInMonth) {
return const Expanded(child: SizedBox.shrink());
}
final dayDate =
DateTime(now.year, now.month, dayNumber);
final isToday = dayNumber == now.day;
final hasSchedule = _hasScheduleOn(dayDate);
return Expanded(
child: Container(
margin: const EdgeInsets.all(1),
decoration: isToday
? BoxDecoration(
border: Border.all(
color: Theme.of(context).colorScheme.primary,
width: 2,
),
shape: BoxShape.circle,
)
: null,
child: Stack(
alignment: Alignment.center,
children: [
Center(
child: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
'$dayNumber',
style: TextStyle(
color: isToday
? Theme.of(context).colorScheme.primary
: (isSunday ? Colors.redAccent : Colors.white),
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
),
),
if (hasSchedule) // Show indicator even if it's today
Positioned(
bottom: 2,
child: Container(
width: 14,
height: 4,
decoration: BoxDecoration(
color: Colors.yellowAccent,
borderRadius: BorderRadius.circular(2),
),
),
),
],
),
),
);
}),
),
);
}),
),
),
],
),
);
}
}