import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../../core/di/injection.dart'; import '../../../../core/theme/design_tokens.dart'; import '../calendar_state_manager.dart'; import '../calendar_time_utils.dart'; import '../widgets/bottom_dock.dart'; class CalendarDayWeekScreen extends StatefulWidget { final DateTime? initialDate; final bool resetToToday; const CalendarDayWeekScreen({ super.key, this.initialDate, this.resetToToday = false, }); @override State createState() => _CalendarDayWeekScreenState(); } class _CalendarDayWeekScreenState extends State { static const double _dayItemWidth = 44; static const double _dayItemGap = 12; late final CalendarStateManager _calendarManager; late DateTime _selectedDate; late List _monthDates; final ScrollController _dayStripController = ScrollController(); @override void initState() { super.initState(); _calendarManager = sl(); if (widget.resetToToday) { _calendarManager.resetToToday(); } _selectedDate = _calendarManager.selectedDate; _updateMonthDates(); WidgetsBinding.instance.addPostFrameCallback((_) { _scrollToSelectedDate(); }); } void _updateMonthDates() { _monthDates = monthDatesFor(_selectedDate); } @override void dispose() { _dayStripController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.todoBg, body: SafeArea( child: Column( children: [ _buildHeader(), Expanded( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.only( left: AppSpacing.lg, right: AppSpacing.lg, top: 2, bottom: 104, ), child: Column( children: [ _buildWeekStrip(), const SizedBox(height: 8), _buildTimelineBoard(), ], ), ), ), ), _buildBottomDock(), ], ), ), ); } Widget _buildHeader() { return SizedBox( height: 68, child: Padding( padding: const EdgeInsets.only(left: 20, right: 20, top: 12, bottom: 8), child: Row( children: [ GestureDetector( onTap: () => context.go('/calendar/month'), child: Container( height: 36, padding: const EdgeInsets.symmetric(horizontal: 10), decoration: BoxDecoration( color: AppColors.messageBtnWrap, borderRadius: BorderRadius.circular(AppRadius.xl), border: Border.all(color: AppColors.messageBtnBorder), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( LucideIcons.chevronLeft, size: 16, color: AppColors.slate700, ), const SizedBox(width: 6), Text( '${_selectedDate.year}年${_selectedDate.month}月', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: AppColors.slate700, ), ), ], ), ), ), ], ), ), ); } Widget _buildWeekStrip() { return SizedBox( height: 86, child: ListView.separated( controller: _dayStripController, scrollDirection: Axis.horizontal, itemCount: _monthDates.length, separatorBuilder: (context, index) => const SizedBox(width: _dayItemGap), itemBuilder: (context, index) { final date = _monthDates[index]; final isSelected = isSameDay(date, _selectedDate); final isWeekend = date.weekday % 7 == 0 || date.weekday % 7 == 6; return GestureDetector( onTap: () { setState(() { _selectedDate = date; }); _calendarManager.setSelectedDate(date); _updateMonthDates(); _scrollToSelectedDate(animate: true); }, child: SizedBox( width: _dayItemWidth, child: _buildDayItem(date, isSelected, isWeekend), ), ); }, ), ); } void _scrollToSelectedDate({bool animate = false}) { if (!_dayStripController.hasClients) { return; } final index = _monthDates.indexWhere( (date) => isSameDay(date, _selectedDate), ); if (index < 0) { return; } final targetCenter = index * (_dayItemWidth + _dayItemGap) + (_dayItemWidth / 2); final viewport = _dayStripController.position.viewportDimension; var offset = targetCenter - (viewport / 2); final max = _dayStripController.position.maxScrollExtent; if (offset < 0) { offset = 0; } if (offset > max) { offset = max; } if (animate) { _dayStripController.animateTo( offset, duration: const Duration(milliseconds: 180), curve: Curves.easeOut, ); return; } _dayStripController.jumpTo(offset); } Widget _buildDayItem(DateTime date, bool isSelected, bool isWeekend) { final dayNames = ['日', '一', '二', '三', '四', '五', '六']; return Column( mainAxisSize: MainAxisSize.min, children: [ Text( dayNames[date.weekday % 7], style: TextStyle( fontSize: 11, color: isWeekend ? AppColors.slate400 : AppColors.slate600, ), ), const SizedBox(height: 2), Text( '${date.day}', style: TextStyle( fontSize: 17, fontWeight: isSelected ? FontWeight.w700 : FontWeight.w600, color: isSelected ? AppColors.blue600 : (isWeekend ? AppColors.slate400 : AppColors.slate900), ), ), ], ); } Widget _buildTimelineBoard() { final now = DateTime.now(); final showCurrent = shouldShowCurrentMarker(_selectedDate, now); final rows = []; for (var hour = 7; hour <= 22; hour++) { rows.add(_buildTimelineRow(formatHour(hour))); if (showCurrent && now.hour == hour) { rows.add(_buildTimelineRow(formatHm(now), isCurrentTime: true)); } } rows.add(_buildTimelineRow(formatHour(24), isDisabled: true)); return Column(children: rows); } Widget _buildTimelineRow( String time, { bool isCurrentTime = false, bool isDisabled = false, }) { return SizedBox( height: 34, child: Row( children: [ SizedBox( width: 44, child: isCurrentTime ? Container( width: 44, height: 18, decoration: BoxDecoration( color: AppColors.red500, borderRadius: BorderRadius.circular(9), ), child: Center( child: Text( time, style: const TextStyle( fontSize: 10, fontWeight: FontWeight.w700, color: Colors.white, ), ), ), ) : Text( time, textAlign: TextAlign.right, style: TextStyle( fontSize: 10, fontWeight: FontWeight.w600, color: isDisabled ? AppColors.slate300 : AppColors.slate400, ), ), ), const SizedBox(width: 8), Expanded( child: isCurrentTime ? Container( height: 2, decoration: BoxDecoration( color: AppColors.red500, borderRadius: BorderRadius.circular(99), ), ) : Container( height: 1, color: isDisabled ? AppColors.blue50 : AppColors.border, ), ), ], ), ); } Widget _buildBottomDock() { return BottomDock( activeTab: DockTab.calendar, onTodoTap: () { _calendarManager.setViewType(CalendarViewType.day); context.push('/todo'); }, onCalendarTap: () { _calendarManager.setViewType(CalendarViewType.day); context.go('/calendar/month'); }, onHomeTap: () => context.go('/home'), ); } }