import 'package:flutter/material.dart'; import 'package:shamsi_date/shamsi_date.dart'; /// DatePicker سفارشی برای تقویم شمسی class JalaliDatePicker extends StatefulWidget { final DateTime? initialDate; final DateTime? firstDate; final DateTime? lastDate; final String? helpText; final ValueChanged? onDateChanged; const JalaliDatePicker({ super.key, this.initialDate, this.firstDate, this.lastDate, this.helpText, this.onDateChanged, }); @override State createState() => _JalaliDatePickerState(); } class _JalaliDatePickerState extends State { late DateTime _selectedDate; late Jalali _selectedJalali; @override void initState() { super.initState(); _selectedDate = widget.initialDate ?? DateTime.now(); _selectedJalali = Jalali.fromDateTime(_selectedDate); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final jalali = Jalali.fromDateTime(_selectedDate); return Dialog( backgroundColor: theme.dialogTheme.backgroundColor, child: Container( width: 350, height: 450, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.dialogTheme.backgroundColor, borderRadius: BorderRadius.circular(16), ), child: Column( children: [ if (widget.helpText != null) Padding( padding: const EdgeInsets.only(bottom: 16), child: Text( widget.helpText!, style: theme.textTheme.titleMedium?.copyWith( color: theme.textTheme.titleMedium?.color, ), ), ), // Selected date display Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: theme.colorScheme.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.calendar_today, color: theme.primaryColor, size: 20, ), const SizedBox(width: 8), Text( '${jalali.year}/${jalali.month.toString().padLeft(2, '0')}/${jalali.day.toString().padLeft(2, '0')}', style: theme.textTheme.titleMedium?.copyWith( color: theme.primaryColor, fontWeight: FontWeight.bold, ), ), ], ), ), // Calendar Expanded( child: _buildCalendar(), ), // Buttons const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: Text( 'انصراف', style: TextStyle(color: theme.textTheme.bodyMedium?.color), ), ), const SizedBox(width: 8), ElevatedButton( onPressed: () { widget.onDateChanged?.call(_selectedDate); Navigator.of(context).pop(_selectedDate); }, child: const Text('تایید'), ), ], ), ], ), ), ); } Widget _buildCalendar() { return _CustomPersianCalendar( initialDate: _selectedJalali, firstDate: Jalali.fromDateTime(widget.firstDate ?? DateTime(1900)), lastDate: Jalali.fromDateTime(widget.lastDate ?? DateTime(2100)), onDateChanged: (jalali) { setState(() { _selectedJalali = jalali; _selectedDate = jalali.toDateTime(); }); }, ); } } /// تابع کمکی برای نمایش Jalali DatePicker Future showJalaliDatePicker({ required BuildContext context, required DateTime initialDate, required DateTime firstDate, required DateTime lastDate, String? helpText, }) { return showDialog( context: context, builder: (context) => JalaliDatePicker( initialDate: initialDate, firstDate: firstDate, lastDate: lastDate, helpText: helpText, ), ); } /// Custom Persian Calendar Widget with proper Persian month names class _CustomPersianCalendar extends StatefulWidget { final Jalali initialDate; final Jalali firstDate; final Jalali lastDate; final ValueChanged onDateChanged; const _CustomPersianCalendar({ required this.initialDate, required this.firstDate, required this.lastDate, required this.onDateChanged, }); @override State<_CustomPersianCalendar> createState() => _CustomPersianCalendarState(); } class _CustomPersianCalendarState extends State<_CustomPersianCalendar> { late Jalali _currentDate; late Jalali _selectedDate; // Persian month names static const List _monthNames = [ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند' ]; // Persian day names (abbreviated) static const List _dayNames = [ 'ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج' ]; @override void initState() { super.initState(); _currentDate = widget.initialDate; _selectedDate = widget.initialDate; } void _previousMonth() { setState(() { if (_currentDate.month == 1) { _currentDate = Jalali(_currentDate.year - 1, 12, 1); } else { _currentDate = Jalali(_currentDate.year, _currentDate.month - 1, 1); } }); } void _nextMonth() { setState(() { if (_currentDate.month == 12) { _currentDate = Jalali(_currentDate.year + 1, 1, 1); } else { _currentDate = Jalali(_currentDate.year, _currentDate.month + 1, 1); } }); } void _selectDate(Jalali date) { setState(() { _selectedDate = date; }); widget.onDateChanged(date); } @override Widget build(BuildContext context) { final theme = Theme.of(context); // Get the first day of the month and calculate the starting day final firstDayOfMonth = Jalali(_currentDate.year, _currentDate.month, 1); final lastDayOfMonth = Jalali(_currentDate.year, _currentDate.month, _currentDate.monthLength); // Calculate the starting weekday (0 = Saturday, 6 = Friday) // Convert Jalali to DateTime to get weekday, then adjust for Persian calendar final gregorianFirstDay = firstDayOfMonth.toDateTime(); final startWeekday = (gregorianFirstDay.weekday + 1) % 7; // Adjust for Persian week start (Saturday) return Column( children: [ // Month/Year header Container( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( onPressed: _previousMonth, icon: const Icon(Icons.chevron_left), ), Text( '${_monthNames[_currentDate.month - 1]} ${_currentDate.year}', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, ), ), IconButton( onPressed: _nextMonth, icon: const Icon(Icons.chevron_right), ), ], ), ), // Day names header Row( children: _dayNames.map((day) => Expanded( child: Center( child: Text( day, style: theme.textTheme.bodySmall?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface.withValues(alpha: 0.6), ), ), ), )).toList(), ), const SizedBox(height: 8), // Calendar grid Expanded( child: GridView.builder( physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 7, childAspectRatio: 1.0, ), itemCount: 42, // 6 weeks * 7 days itemBuilder: (context, index) { final dayIndex = index - startWeekday; final day = dayIndex + 1; if (dayIndex < 0 || day > lastDayOfMonth.day.toInt()) { return const SizedBox.shrink(); } final date = Jalali(_currentDate.year, _currentDate.month, day); final isSelected = date.year == _selectedDate.year && date.month == _selectedDate.month && date.day == _selectedDate.day; final isToday = date.year == Jalali.now().year && date.month == Jalali.now().month && date.day == Jalali.now().day; return GestureDetector( onTap: () => _selectDate(date), child: Container( margin: const EdgeInsets.all(2), decoration: BoxDecoration( color: isSelected ? theme.colorScheme.primary : isToday ? theme.colorScheme.primary.withValues(alpha: 0.1) : Colors.transparent, borderRadius: BorderRadius.circular(8), border: isToday && !isSelected ? Border.all(color: theme.colorScheme.primary, width: 1) : null, ), child: Center( child: Text( day.toString(), style: theme.textTheme.bodyMedium?.copyWith( color: isSelected ? theme.colorScheme.onPrimary : isToday ? theme.colorScheme.primary : theme.colorScheme.onSurface, fontWeight: isSelected || isToday ? FontWeight.bold : FontWeight.normal, ), ), ), ), ); }, ), ), ], ); } }