| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:intl/intl.dart'; | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  | import 'package:qhd_prevention/customWidget/custom_button.dart'; | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class HDatePickerDialog extends StatefulWidget { | 
					
						
							|  |  |  |   final DateTime initialDate; | 
					
						
							|  |  |  |   final VoidCallback onCancel; | 
					
						
							|  |  |  |   final ValueChanged<DateTime> onConfirm; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const HDatePickerDialog({ | 
					
						
							|  |  |  |     Key? key, | 
					
						
							|  |  |  |     required this.initialDate, | 
					
						
							|  |  |  |     required this.onCancel, | 
					
						
							|  |  |  |     required this.onConfirm, | 
					
						
							|  |  |  |   }) : super(key: key); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   _HDatePickerDialogState createState() => _HDatePickerDialogState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _HDatePickerDialogState extends State<HDatePickerDialog> { | 
					
						
							|  |  |  |   late DateTime _displayedMonth; | 
					
						
							|  |  |  |   late DateTime _selectedDate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |     _selectedDate = widget.initialDate; | 
					
						
							|  |  |  |     _displayedMonth = DateTime(widget.initialDate.year, widget.initialDate.month); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _prevMonth() { | 
					
						
							|  |  |  |     setState(() { | 
					
						
							|  |  |  |       _displayedMonth = DateTime(_displayedMonth.year, _displayedMonth.month - 1); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _nextMonth() { | 
					
						
							|  |  |  |     setState(() { | 
					
						
							|  |  |  |       _displayedMonth = DateTime(_displayedMonth.year, _displayedMonth.month + 1); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |     final daysInMonth = DateUtils.getDaysInMonth(_displayedMonth.year, _displayedMonth.month); | 
					
						
							|  |  |  |     final firstWeekday = DateTime(_displayedMonth.year, _displayedMonth.month, 1).weekday; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return GestureDetector( | 
					
						
							|  |  |  |       onTap: widget.onCancel, | 
					
						
							|  |  |  |       child: Material( | 
					
						
							|  |  |  |         color: Colors.black54, | 
					
						
							|  |  |  |         child: Center( | 
					
						
							|  |  |  |           child: GestureDetector( | 
					
						
							|  |  |  |             onTap: () {}, // 拦截点击
 | 
					
						
							|  |  |  |             child: Container( | 
					
						
							|  |  |  |               width: 320, | 
					
						
							|  |  |  |               padding: const EdgeInsets.all(16), | 
					
						
							|  |  |  |               decoration: BoxDecoration( | 
					
						
							|  |  |  |                 color: Colors.white, | 
					
						
							|  |  |  |                 borderRadius: BorderRadius.circular(8), | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |               child: Column( | 
					
						
							|  |  |  |                 mainAxisSize: MainAxisSize.min, | 
					
						
							|  |  |  |                 children: [ | 
					
						
							|  |  |  |                   // 顶部显示选中日期
 | 
					
						
							|  |  |  |                   Text( | 
					
						
							|  |  |  |                     DateFormat('yyyy-MM-dd').format(_selectedDate), | 
					
						
							|  |  |  |                     style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   const SizedBox(height: 16), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // 月份选择
 | 
					
						
							|  |  |  |                   Row( | 
					
						
							|  |  |  |                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |                     children: [ | 
					
						
							|  |  |  |                       IconButton( | 
					
						
							|  |  |  |                         icon: const Icon(Icons.chevron_left), | 
					
						
							|  |  |  |                         onPressed: _prevMonth, | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       Text( | 
					
						
							|  |  |  |                         DateFormat('yyyy 年 MM 月').format(_displayedMonth), | 
					
						
							|  |  |  |                         style: const TextStyle(fontSize: 16), | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                       IconButton( | 
					
						
							|  |  |  |                         icon: const Icon(Icons.chevron_right), | 
					
						
							|  |  |  |                         onPressed: _nextMonth, | 
					
						
							|  |  |  |                       ), | 
					
						
							|  |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // 星期标题
 | 
					
						
							|  |  |  |                   Row( | 
					
						
							|  |  |  |                     mainAxisAlignment: MainAxisAlignment.spaceAround, | 
					
						
							|  |  |  |                     children: ['一', '二', '三', '四', '五', '六', '日'] | 
					
						
							|  |  |  |                         .map((d) => Expanded( | 
					
						
							|  |  |  |                       child: Center(child: Text(d)), | 
					
						
							|  |  |  |                     )) | 
					
						
							|  |  |  |                         .toList(), | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                   SizedBox(height: 10,), | 
					
						
							|  |  |  |                   // 日历网格
 | 
					
						
							|  |  |  |                   GridView.builder( | 
					
						
							|  |  |  |                     shrinkWrap: true, | 
					
						
							|  |  |  |                     physics: const NeverScrollableScrollPhysics(), | 
					
						
							|  |  |  |                     gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( | 
					
						
							|  |  |  |                       crossAxisCount: 7, | 
					
						
							|  |  |  |                       childAspectRatio: 1.2, | 
					
						
							|  |  |  |                       crossAxisSpacing: 4, | 
					
						
							|  |  |  |                       mainAxisSpacing: 4, | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     itemCount: daysInMonth + firstWeekday - 1, | 
					
						
							|  |  |  |                     itemBuilder: (context, index) { | 
					
						
							|  |  |  |                       if (index < firstWeekday - 1) { | 
					
						
							|  |  |  |                         return const SizedBox(); | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  |                       final day = index - firstWeekday + 2; | 
					
						
							|  |  |  |                       final date = DateTime(_displayedMonth.year, _displayedMonth.month, day); | 
					
						
							|  |  |  |                       final isSelected = DateUtils.isSameDay(date, _selectedDate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                       return GestureDetector( | 
					
						
							|  |  |  |                         onTap: () { | 
					
						
							|  |  |  |                           setState(() { | 
					
						
							|  |  |  |                             _selectedDate = date; | 
					
						
							|  |  |  |                           }); | 
					
						
							|  |  |  |                         }, | 
					
						
							|  |  |  |                         child: Container( | 
					
						
							|  |  |  |                           decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |                             color: isSelected ? Colors.blue : null, | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                             shape: BoxShape.circle, | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                           child: Center( | 
					
						
							|  |  |  |                             child: Text( | 
					
						
							|  |  |  |                               '$day', | 
					
						
							|  |  |  |                               style: TextStyle( | 
					
						
							|  |  |  |                                 color: isSelected ? Colors.white : Colors.black87, | 
					
						
							|  |  |  |                               ), | 
					
						
							|  |  |  |                             ), | 
					
						
							|  |  |  |                           ), | 
					
						
							|  |  |  |                         ), | 
					
						
							|  |  |  |                       ); | 
					
						
							|  |  |  |                     }, | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   const SizedBox(height: 16), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // 按钮
 | 
					
						
							|  |  |  |                   Row( | 
					
						
							|  |  |  |                     mainAxisAlignment: MainAxisAlignment.spaceEvenly, | 
					
						
							|  |  |  |                     children: [ | 
					
						
							| 
									
										
										
										
											2025-07-28 14:22:07 +08:00
										 |  |  |                       Expanded(child: CustomButton(text: '取消',height: 40,backgroundColor: Colors.grey.shade500, | 
					
						
							|  |  |  |                           onPressed: widget.onCancel),), | 
					
						
							|  |  |  |                       SizedBox(width: 30,), | 
					
						
							|  |  |  |                       Expanded(child: CustomButton(text: '确定',height: 40,backgroundColor: Colors.blue, | 
					
						
							|  |  |  |                           onPressed: () => widget.onConfirm(_selectedDate)),) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-11 11:03:21 +08:00
										 |  |  |                     ], | 
					
						
							|  |  |  |                   ), | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |               ), | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |