import 'package:flutter/material.dart'; import '../../common/settings.dart'; class AdvancedDateRangePicker extends StatefulWidget { final DateTimeRange? initialRange; const AdvancedDateRangePicker({super.key, this.initialRange}); @override State createState() => _AdvancedDateRangePickerState(); } class _AdvancedDateRangePickerState extends State { late DateTime _currentMonth; DateTime? _start; DateTime? _end; late PageController _pageController; int _initialPage = 500; // large buffer for infinite scroll @override void initState() { super.initState(); _initialPage = 500; _pageController = PageController(initialPage: _initialPage); _currentMonth = DateTime(DateTime.now().year, DateTime.now().month, 1); if (widget.initialRange != null) { _start = widget.initialRange!.start; _end = widget.initialRange!.end; } } @override Widget build(BuildContext context) { return Dialog( backgroundColor: Colors.white, // White background shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: Container( padding: const EdgeInsets.all(5), width: 420, child: Column( mainAxisSize: MainAxisSize.min, children: [ _buildHeader(), const SizedBox(height: 12), _buildWeekDays(), SizedBox( height: 270, child: PageView.builder( controller: _pageController, onPageChanged: (pageIndex) { //to update the current month state when user swipes setState(() { int diff = pageIndex - _initialPage; _currentMonth = DateTime( DateTime.now().year, DateTime.now().month + diff, 1, ); }); }, itemBuilder: (context, index) { //to build the calendar for each month int diff = index - _initialPage; DateTime monthToShow = DateTime( DateTime.now().year, DateTime.now().month + diff, 1, ); return _buildCalendarGrid(monthToShow); }, ), ), const SizedBox(height: 12), _buildSelectionText(), const SizedBox(height: 16), _buildFooter() ], ), ), ); } // ----------------------------------------------------------- // 🔵 HEADER (Month – Year + Arrows) // ----------------------------------------------------------- Widget _buildHeader() { return Column( children: [ // Title const Padding( padding: EdgeInsets.symmetric(vertical: 8.0), child: Text( "Select a Date Range", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black, ), ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ IconButton( icon: const Icon(Icons.arrow_left, color: Colors.black), onPressed: () { _pageController.previousPage( duration: const Duration(milliseconds: 250), curve: Curves.easeOut, ); }, ), GestureDetector( onTap: () => _showYearPicker(context), child: Text( "${_monthName(_currentMonth.month)} ${_currentMonth.year}", style: fontTextStyle(18, Colors.black, FontWeight.w400), ), ), IconButton( icon: const Icon(Icons.arrow_right, color: Colors.black), onPressed: () { _pageController.nextPage( duration: const Duration(milliseconds: 250), curve: Curves.easeOut, ); }, ), ], ) ], ); } // ----------------------------------------------------------- // 🔵 WEEK DAY ROW // ----------------------------------------------------------- Widget _buildWeekDays() { const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: days .map((d) => Expanded( child: Center( child: Text( d, style: fontTextStyle(14, Colors.black, FontWeight.w600), ), ), )) .toList(), ); } // ----------------------------------------------------------- // 🔵 BUILD CALENDAR GRID // ----------------------------------------------------------- Widget _buildCalendarGrid(DateTime month) { List rows = []; DateTime firstDay = DateTime(month.year, month.month, 1); int startingWeekday = firstDay.weekday % 7; // Sunday=0 int daysInMonth = DateTime(month.year, month.month + 1, 0).day; List dayCells = []; // Empty cells before month starts for (int i = 0; i < startingWeekday; i++) { dayCells.add(const Expanded(child: SizedBox())); } // Month days for (int d = 1; d <= daysInMonth; d++) { DateTime date = DateTime(month.year, month.month, d); bool isToday = _isSame(date, DateTime.now()); // Highlight today bool isSelectedStart = _isSame(date, _start); bool isSelectedEnd = _isSame(date, _end); bool inRange = _start != null && _end != null && date.isAfter(_start!) && date.isBefore(_end!); dayCells.add( Expanded( child: GestureDetector( onTap: () => _onDateTap(date), child: Container( margin: const EdgeInsets.all(4), padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: isSelectedStart || isSelectedEnd ? Colors.black : inRange ? Colors.black12 : Colors.transparent, shape: BoxShape.circle, border: isToday && !isSelectedStart && !isSelectedEnd ? Border.all(color: Colors.blueAccent, width: 2) // Highlight today : null, ), child: Center( child: Text( "$d", style: TextStyle( color: isSelectedStart || isSelectedEnd ? Colors.white : Colors.black, fontWeight: isToday ? FontWeight.bold : FontWeight.normal, ), ), ), ), ), ), ); // Create a row after each week if ((dayCells.length) % 7 == 0) { rows.add(Row(children: dayCells)); dayCells = []; } } // Last row fill remaining empty cells if (dayCells.isNotEmpty) { while (dayCells.length < 7) { dayCells.add(const Expanded(child: SizedBox())); } rows.add(Row(children: dayCells)); } return Column(children: rows); } // ----------------------------------------------------------- // 🔵 DATE TAP LOGIC // ----------------------------------------------------------- void _onDateTap(DateTime date) { setState(() { if (_start == null || (_start != null && _end != null)) { _start = date; _end = null; } else if (date.isBefore(_start!)) { _end = _start; _start = date; } else { _end = date; } }); } // ----------------------------------------------------------- // 🔵 SELECTED TEXT // ----------------------------------------------------------- // Widget _buildSelectionText() { // return Text( // _start == null // ? "Choose Start Date" // : _end == null // ? "Choose End Date" // : "Selected: ${_fmt(_start!)} → ${_fmt(_end!)}", // style: fontTextStyle(14, Colors.blueAccent, FontWeight.w600), // ); // } Widget _buildSelectionText() { if (_start == null || _end == null) { return const SizedBox(); // Show nothing } return Text( "Selected: ${_fmt(_start!)} → ${_fmt(_end!)}", style: fontTextStyle(14, Colors.blueAccent, FontWeight.w600), ); } // ----------------------------------------------------------- // 🔵 FOOTER BUTTONS // ----------------------------------------------------------- Widget _buildFooter() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ TextButton( child: Text("Cancel", style: fontTextStyle(14, Colors.black, FontWeight.w600)), onPressed: () => Navigator.pop(context), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.black, ), onPressed: (_start != null && _end != null) ? () => Navigator.pop( context, DateTimeRange(start: _start!, end: _end!), ) : null, child: Text("Apply", style: fontTextStyle(14, Colors.white, FontWeight.bold)), ), ], ); } // ----------------------------------------------------------- // 🔧 HELPERS // ----------------------------------------------------------- bool _isSame(DateTime? a, DateTime? b) { if (a == null || b == null) return false; return a.year == b.year && a.month == b.month && a.day == b.day; } String _fmt(DateTime d) => "${d.year}-${d.month}-${d.day}"; String _monthName(int m) { const names = [ "January","February","March","April","May","June", "July","August","September","October","November","December" ]; return names[m - 1]; } // ----------------------------------------------------------- // 🔵 YEAR PICKER SHEET // ----------------------------------------------------------- void _showYearPicker(BuildContext context) { int currentYear = DateTime.now().year; int startYear = currentYear - 25; int endYear = currentYear + 25; int selectedYear = _currentMonth.year; ScrollController scrollController = ScrollController( initialScrollOffset: (selectedYear - startYear) * 48.0, // 56 is approx ListTile height ); showDialog( context: context, builder: (_) => Dialog( backgroundColor: Colors.white, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: SizedBox( height: 300, child: ListView.builder( controller: scrollController, itemCount: endYear - startYear + 1, itemBuilder: (context, index) { int y = startYear + index; bool isSelected = y == selectedYear; return Center( child: GestureDetector( onTap: () { setState(() { _currentMonth = DateTime(y, _currentMonth.month, 1); }); Navigator.pop(context); }, child: Container( margin: const EdgeInsets.symmetric(vertical: 4), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), decoration: BoxDecoration( color: isSelected ? Colors.black : Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.black12), ), child: Text( "$y", style: fontTextStyle( 16, isSelected ? Colors.white : Colors.black, FontWeight.bold, ), ), ), ), ); }, ), ), ), ); } //To clear the selected dates inside your custom calendar grid void _clearCalendarSelection() { setState(() { _start = null; _end = null; }); } }