diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index ebac476d..4d8d1650 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -211,7 +211,7 @@ class ApiClientImp implements ApiClient { } // body['TokenID'] = "@dm!n"; - // body['PatientID'] = 3310954; + // body['PatientID'] = 945786; // body['PatientID'] = 53320; // body['PatientTypeID'] = 1; // body['PatientOutSA'] = 0; diff --git a/lib/core/utils/zoom_config.dart b/lib/core/utils/zoom_config.dart index 09370c00..8262e83d 100644 --- a/lib/core/utils/zoom_config.dart +++ b/lib/core/utils/zoom_config.dart @@ -2,3 +2,9 @@ const Map configs = { 'ZOOM_SDK_KEY': 'b9T74nhfTg-ioP9urm970A', 'ZOOM_SDK_SECRET': 'KOzmjBNXQ1f4IPHpnngfL29uZvJMufSy2Fk8', }; + + +// const Map configs = { +// 'ZOOM_SDK_KEY': 'jYHoRpSUMTTefOwLq94Kyf1Kak513TUHpu78', +// 'ZOOM_SDK_SECRET': 'MOqi1zc18VFOMEaBDshFSHKLB0C2n9M0K48H', +// }; diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 3caed75a..db300d98 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -58,6 +58,7 @@ import 'package:hmg_patient_app_new/presentation/notifications/notifications_lis import 'package:hmg_patient_app_new/presentation/offers_and_discounts/offers_and_discounts_page.dart'; import 'package:hmg_patient_app_new/presentation/offers_and_discounts/widgets/offers_and_discounts.dart'; import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_doctor.dart'; +import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart'; import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.dart'; @@ -291,6 +292,15 @@ class _LandingPageState extends State { page: FamilyMedicalScreen(), ), ); + + // Navigator.pushReplacementNamed( + // // context, + // GetIt.instance().navigatorKey.currentContext!, + // AppRoutes.zoomCallPage, + // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1), + // arguments: CallArguments("yosemite-338", "123", "Patient", "40", "0", true, 1), + // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)), + // ); }, name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), imageWidget: Selector( @@ -314,6 +324,13 @@ class _LandingPageState extends State { onPressed: () async { await authVM.onLoginPressed(); + // Navigator.pushReplacementNamed( + // // context, + // GetIt.instance().navigatorKey.currentContext!, + // AppRoutes.zoomCallPage, + // arguments: CallArguments("lake-tahoe-289", "123", "Patient", "40", "0", true, 1), + // ); + // Navigator.of(context).push( // CustomPageRoute( // // page: NotificationsListPage(), diff --git a/lib/presentation/tele_consultation/zoom/call_screen.dart b/lib/presentation/tele_consultation/zoom/call_screen.dart index 64c6c471..1c9db67a 100644 --- a/lib/presentation/tele_consultation/zoom/call_screen.dart +++ b/lib/presentation/tele_consultation/zoom/call_screen.dart @@ -84,7 +84,7 @@ class _CallScreenState extends State { //hide status bar SystemChrome.setEnabledSystemUIMode(SystemUiMode.leanBack); - var circleButtonSize = 65.0; + var circleButtonSize = 65.h; Color backgroundColor = const Color(0xFF232323); Color buttonBackgroundColor = const Color.fromRGBO(0, 0, 0, 0.6); Color chatTextColor = const Color(0xFFAAAAAA); @@ -178,12 +178,12 @@ class _CallScreenState extends State { // "Join", // arguments: JoinArguments(args.isJoin, sessionName.value, sessionPassword.value, args.displayName, args.sessionIdleTimeoutMins, args.role), // ); - Navigator.pushAndRemoveUntil( - context, - CustomPageRoute( - page: LandingNavigation(), - ), - (r) => false); + // Navigator.pushAndRemoveUntil( + // context, + // CustomPageRoute( + // page: LandingNavigation(), + // ), + // (r) => false); }); final sessionNeedPasswordListener = eventListener.addListener(EventType.onSessionNeedPassword, (data) async { @@ -1773,16 +1773,17 @@ class _CallScreenState extends State { alignment: Alignment.centerRight, child: FractionallySizedBox( widthFactor: 0.2, - heightFactor: 0.6, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + heightFactor: 0.8, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( onPressed: onPressAudio, - icon: isMuted.value ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/unmute@2x.png") : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/mute@2x.png"), - iconSize: circleButtonSize, - tooltip: isMuted.value == true ? "Unmute" : "Mute", - ), + icon: isMuted.value + ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/unmute@2x.png", width: circleButtonSize.h, height: circleButtonSize.h) + : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/mute@2x.png", width: circleButtonSize.h, height: circleButtonSize.h), + iconSize: circleButtonSize, + ), // IconButton( // onPressed: onPressShare, // icon: isSharing.value ? Image.asset("assets/images/png/zoom/share-off@2x.png") : Image.asset("assets/images/png/zoom/share-on@2x.png"), @@ -1791,9 +1792,11 @@ class _CallScreenState extends State { IconButton( onPressed: onPressVideo, iconSize: circleButtonSize, - icon: isVideoOn.value ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-off@2x.png") : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-on@2x.png"), - ), - Column( + icon: isVideoOn.value + ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-off@2x.png", width: circleButtonSize.h, height: circleButtonSize.h) + : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-on@2x.png", width: circleButtonSize.h, height: circleButtonSize.h), + ), + Column( children: [ IconButton( onPressed: () async { @@ -1808,7 +1811,8 @@ class _CallScreenState extends State { ), ], ), - )), + ), + ), // Container( // margin: const EdgeInsets.only(left: 16, right: 16, bottom: 40, top: 10), // alignment: Alignment.bottomCenter, diff --git a/lib/widgets/date_range_selector/date_range_calender.dart b/lib/widgets/date_range_selector/date_range_calender.dart index 463f516a..a544f010 100644 --- a/lib/widgets/date_range_selector/date_range_calender.dart +++ b/lib/widgets/date_range_selector/date_range_calender.dart @@ -56,8 +56,9 @@ class _DateRangeSelectorState extends State { late DateRangeSelectorRangeViewModel model; PickerViewMode _viewMode = PickerViewMode.date; - // Cache for Hijri conversions to avoid repeated calculations - final Map _hijriCache = {}; + // Prevent Syncfusion's initial onViewChanged callback from scheduling an + // unnecessary second build of the same month. + late int _displayedMonthKey; // Track the current Hijri month/year being displayed (for header display accuracy) HijriGregDate? _currentHijriDisplay; @@ -65,10 +66,14 @@ class _DateRangeSelectorState extends State { @override void initState() { _calendarController = DateRangePickerController(); + final today = DateTime.now(); + _displayedMonthKey = _monthKey(today); + if (widget.designType == CalendarDesignType.designV2) { + _calendarController.displayDate = today; + } scheduleMicrotask(() { if (widget.designType == CalendarDesignType.designV2) { // For V2, select today's date by default - final today = DateTime.now(); _calendarController.selectedDate = today; model.updateSelectedDate(today); } else { @@ -83,7 +88,9 @@ class _DateRangeSelectorState extends State { Widget build(BuildContext context) { model = Provider.of(context); - _calendarController.selectedRange = PickerDateRange(model.fromDate, model.toDate); + if (widget.designType != CalendarDesignType.designV2) { + _calendarController.selectedRange = PickerDateRange(model.fromDate, model.toDate); + } return widget.designType == CalendarDesignType.designV2 ? _buildDesignV2(widget.btnTitle) : _buildDefaultUI(); } @@ -273,18 +280,17 @@ class _DateRangeSelectorState extends State { tabs: [CustomTabBarModel(null, LocaleKeys.gregorianDate.tr()), CustomTabBarModel(null, LocaleKeys.hijriDate.tr())], onTabChange: (index) { final calendarModel = Provider.of(context, listen: false); + if (calendarModel.getSelectedTabIndex == index) return; + + // Update local fields before notifying the provider so + // the calendar changes with a single rebuild. + _viewMode = PickerViewMode.date; + _currentHijriDisplay = null; calendarModel.setTabIndex(index); // Notify parent widget about calendar type change (e.g., AuthenticationViewModel) final isGregorian = index == 0; widget.onCalendarTypeChanged?.call(isGregorian); - print('📅 Calendar type changed: ${isGregorian ? "Gregorian" : "Hijri"}'); - - // Reset view mode and Hijri display when switching calendar types - setState(() { - _viewMode = PickerViewMode.date; - _currentHijriDisplay = null; - }); }, ), ), @@ -306,12 +312,17 @@ class _DateRangeSelectorState extends State { child: Material( color: AppColors.whiteColor, // Rebuild when view mode or calendar type changes + // Syncfusion caches its rendered date cells. Include + // the calendar type in the key so Gregorian/Hijri day + // labels refresh immediately when the tab changes. + // Hijri conversion is now constant-time, so this + // targeted recreation remains responsive. key: ValueKey('${calendarModel.getSelectedTabIndex}-$_viewMode'), child: _viewMode == PickerViewMode.month ? _buildMonthPicker(isArabic, calendarModel) : _viewMode == PickerViewMode.year - ? _buildYearPicker(isArabic, calendarModel) - : (calendarModel.isHijri ? _buildHijriCalendar(isArabic) : _buildGregorianCalendar(isArabic)), + ? _buildYearPicker(isArabic, calendarModel) + : (calendarModel.isHijri ? _buildHijriCalendar(isArabic) : _buildGregorianCalendar(isArabic)), ), ), ], @@ -343,6 +354,8 @@ class _DateRangeSelectorState extends State { // Build Gregorian calendar (Design V2 - Single Date Selection) Widget _buildGregorianCalendar(bool isArabic) { + final today = DateTime.now(); + return SfDateRangePicker( controller: _calendarController, selectionMode: DateRangePickerSelectionMode.single, @@ -366,7 +379,7 @@ class _DateRangeSelectorState extends State { // Custom cell builder for square border selection cellBuilder: (BuildContext context, DateRangePickerCellDetails cellDetails) { if (cellDetails.date != DateTime(0)) { - final isToday = cellDetails.date.day == DateTime.now().day && cellDetails.date.month == DateTime.now().month && cellDetails.date.year == DateTime.now().year; + final isToday = cellDetails.date.day == today.day && cellDetails.date.month == today.month && cellDetails.date.year == today.year; final isSelected = _calendarController.selectedDate != null && cellDetails.date.day == _calendarController.selectedDate!.day && cellDetails.date.month == _calendarController.selectedDate!.month && @@ -414,12 +427,7 @@ class _DateRangeSelectorState extends State { ), ), onViewChanged: (DateRangePickerViewChangedArgs args) { - // Trigger rebuild when month changes - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - setState(() {}); - } - }); + _handleCalendarViewChanged(args); }, onSelectionChanged: (DateRangePickerSelectionChangedArgs args) { if (args.value is DateTime) { @@ -433,105 +441,155 @@ class _DateRangeSelectorState extends State { ); } - // Build Hijri calendar (Design V2 - Single Date Selection) - // Note: SfDateRangePicker doesn't have built-in Hijri support - // Using Gregorian calendar with Hijri date conversion in the model + // Build one complete Hijri month instead of relabelling the dates in a + // Gregorian month. A Gregorian month overlaps two Hijri months, which would + // otherwise produce sequences such as 16...30, 1...17 in the same grid. Widget _buildHijriCalendar(bool isArabic) { final calendarModel = Provider.of(context, listen: false); + final today = DateTime.now(); + final displayedGregorian = _calendarController.displayDate ?? today; + final displayedHijri = _currentHijriDisplay ?? calendarModel.gregorianToHijri(displayedGregorian); + final firstHijriDay = HijriGregDate(day: 1, month: displayedHijri.month, year: displayedHijri.year); + final firstGregorianDay = calendarModel.hijriToGregorian(firstHijriDay); + final leadingEmptyCells = firstGregorianDay.weekday % DateTime.daysPerWeek; + final daysInMonth = calendarModel.getDaysInMonth(displayedHijri.year, displayedHijri.month); + final selectedDate = _calendarController.selectedDate; + const weekdays = [ + DateTime.sunday, + DateTime.monday, + DateTime.tuesday, + DateTime.wednesday, + DateTime.thursday, + DateTime.friday, + DateTime.saturday, + ]; - return SfDateRangePicker( - controller: _calendarController, - selectionMode: DateRangePickerSelectionMode.single, - showNavigationArrow: false, - headerHeight: 0, - backgroundColor: AppColors.whiteColor, - monthViewSettings: DateRangePickerMonthViewSettings( - viewHeaderStyle: DateRangePickerViewHeaderStyle( - backgroundColor: AppColors.whiteColor, - textStyle: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w600, - letterSpacing: -0.46, - color: AppColors.textColor, + return Column( + children: [ + SizedBox( + height: 32.h, + child: Row( + children: weekdays + .map( + (weekday) => Expanded( + child: Center( + child: Text( + calendarModel.getWeekdayNameLocalized(weekday, isArabic), + style: TextStyle( + fontSize: 12.f, + fontWeight: FontWeight.w600, + letterSpacing: -0.46, + color: AppColors.textColor, + ), + ), + ), + ), + ) + .toList(), ), ), - showTrailingAndLeadingDates: false, - dayFormat: "EEE", - ), - cellBuilder: (BuildContext context, DateRangePickerCellDetails cellDetails) { - if (cellDetails.date != DateTime(0)) { - // Use cached Hijri conversion - final dateKey = '${cellDetails.date.year}-${cellDetails.date.month}-${cellDetails.date.day}'; - final hijriDate = _hijriCache.putIfAbsent(dateKey, () => calendarModel.gregorianToHijri(cellDetails.date)); + Expanded( + child: GridView.builder( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: DateTime.daysPerWeek, + mainAxisExtent: 36.h, + mainAxisSpacing: 4.h, + ), + itemCount: 42, + itemBuilder: (context, index) { + final hijriDay = index - leadingEmptyCells + 1; + if (hijriDay < 1 || hijriDay > daysInMonth) { + return const SizedBox.shrink(); + } - final isToday = cellDetails.date.day == DateTime.now().day && cellDetails.date.month == DateTime.now().month && cellDetails.date.year == DateTime.now().year; - final isSelected = _calendarController.selectedDate != null && - cellDetails.date.day == _calendarController.selectedDate!.day && - cellDetails.date.month == _calendarController.selectedDate!.month && - cellDetails.date.year == _calendarController.selectedDate!.year; + final hijriDate = HijriGregDate(day: hijriDay, month: displayedHijri.month, year: displayedHijri.year); + final gregorianDate = calendarModel.hijriToGregorian(hijriDate); + final isToday = _isSameDate(gregorianDate, today); + final isSelected = selectedDate != null && _isSameDate(gregorianDate, selectedDate); - return Container( - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.h), - border: isSelected ? Border.all(color: AppColors.primaryRedColor, width: 2) : null, - color: Colors.transparent, - ), - child: Text( - hijriDate.day.toString(), - style: TextStyle( - fontFamily: "Poppins", - fontSize: 12.f, - color: AppColors.textColor, - fontWeight: isToday ? FontWeight.bold : (isSelected ? FontWeight.w600 : FontWeight.normal), - ), - ), - ); - } - return Container(); - }, - selectionShape: DateRangePickerSelectionShape.rectangle, - selectionRadius: 8.h, - selectionColor: Colors.transparent, - selectionTextStyle: TextStyle( - fontFamily: "Poppins", - color: AppColors.textColor, - fontWeight: FontWeight.w600, - ), - todayHighlightColor: Colors.transparent, - monthCellStyle: DateRangePickerMonthCellStyle( - textStyle: TextStyle( - fontFamily: "Poppins", - fontSize: 12.f, - color: AppColors.textColor, - ), - todayTextStyle: TextStyle( - fontFamily: "Poppins", - color: AppColors.textColor, - fontWeight: FontWeight.bold, + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + _calendarController.selectedDate = gregorianDate; + setState(() { + start = gregorianDate; + end = gregorianDate; + }); + model.updateSelectedDate(gregorianDate); + }, + child: Container( + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.h), + border: isSelected ? Border.all(color: AppColors.primaryRedColor, width: 2) : null, + color: Colors.transparent, + ), + child: Text( + hijriDay.toString(), + style: TextStyle( + fontFamily: "Poppins", + fontSize: 12.f, + color: AppColors.textColor, + fontWeight: isToday ? FontWeight.bold : (isSelected ? FontWeight.w600 : FontWeight.normal), + ), + ), + ), + ); + }, + ), ), - ), - onViewChanged: (DateRangePickerViewChangedArgs args) { - // Clear cache when month changes to avoid stale data - _hijriCache.clear(); - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) { - setState(() {}); - } - }); - }, - onSelectionChanged: (DateRangePickerSelectionChangedArgs args) { - if (args.value is DateTime) { - setState(() { - start = args.value; - end = args.value; - }); - model.updateSelectedDate(args.value); - } - }, + ], ); } + bool _isSameDate(DateTime first, DateTime second) { + return first.year == second.year && first.month == second.month && first.day == second.day; + } + + int _monthKey(DateTime date) => (date.year * 100) + date.month; + + void _changeHijriMonth(DateRangCalenderModel calendarModel, int monthDelta) { + final displayedDate = _calendarController.displayDate ?? DateTime.now(); + final currentHijri = _currentHijriDisplay ?? calendarModel.gregorianToHijri(displayedDate); + final zeroBasedMonth = (currentHijri.year * 12) + currentHijri.month - 1 + monthDelta; + final nextHijriDate = HijriGregDate( + day: 1, + month: (zeroBasedMonth % 12) + 1, + year: zeroBasedMonth ~/ 12, + ); + final nextGregorianDate = calendarModel.hijriToGregorian(nextHijriDate); + + setState(() { + _currentHijriDisplay = nextHijriDate; + _calendarController.displayDate = nextGregorianDate; + _displayedMonthKey = _monthKey(nextGregorianDate); + }); + } + + void _handleCalendarViewChanged(DateRangePickerViewChangedArgs args) { + final rangeStart = args.visibleDateRange.startDate; + final rangeEnd = args.visibleDateRange.endDate; + final displayedDate = _calendarController.displayDate ?? + (rangeEnd == null + ? rangeStart + : rangeStart?.add(Duration(days: rangeEnd.difference(rangeStart).inDays ~/ 2))); + + if (displayedDate == null) return; + + final newMonthKey = _monthKey(displayedDate); + if (newMonthKey == _displayedMonthKey) return; + + _displayedMonthKey = newMonthKey; + _currentHijriDisplay = null; + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() {}); + } + }); + } + // Build unified calendar header for both Gregorian and Hijri Widget _buildCalendarHeader(bool isArabic, DateRangCalenderModel calendarModel) { final displayedDate = _calendarController.displayDate ?? DateTime.now(); @@ -609,12 +667,10 @@ class _DateRangeSelectorState extends State { icon: Icon(Icons.chevron_left, color: AppColors.primaryRedColor), onPressed: () { if (_viewMode == PickerViewMode.date) { - _calendarController.backward!(); - // Update tracked Hijri display when navigating months if (calendarModel.isHijri) { - setState(() { - _currentHijriDisplay = null; // Will recalculate on next build - }); + _changeHijriMonth(calendarModel, -1); + } else { + _calendarController.backward!(); } } else if (_viewMode == PickerViewMode.year) { // Navigate years backward by 12 @@ -625,7 +681,7 @@ class _DateRangeSelectorState extends State { try { final newHijriDate = HijriGregDate(day: 1, month: hijriDate.month, year: hijriDate.year - 12); final newGregorianDate = calendarModel.hijriToGregorian(newHijriDate); - _calendarController.displayDate = DateTime(newGregorianDate.year, newGregorianDate.month, 1); + _calendarController.displayDate = newGregorianDate; _currentHijriDisplay = newHijriDate; } catch (e) { // Fallback @@ -645,12 +701,10 @@ class _DateRangeSelectorState extends State { icon: Icon(Icons.chevron_right, color: AppColors.primaryRedColor), onPressed: () { if (_viewMode == PickerViewMode.date) { - _calendarController.forward!(); - // Update tracked Hijri display when navigating months if (calendarModel.isHijri) { - setState(() { - _currentHijriDisplay = null; // Will recalculate on next build - }); + _changeHijriMonth(calendarModel, 1); + } else { + _calendarController.forward!(); } } else if (_viewMode == PickerViewMode.year) { // Navigate years forward by 12 @@ -661,7 +715,7 @@ class _DateRangeSelectorState extends State { try { final newHijriDate = HijriGregDate(day: 1, month: hijriDate.month, year: hijriDate.year + 12); final newGregorianDate = calendarModel.hijriToGregorian(newHijriDate); - _calendarController.displayDate = DateTime(newGregorianDate.year, newGregorianDate.month, 1); + _calendarController.displayDate = newGregorianDate; _currentHijriDisplay = newHijriDate; } catch (e) { // Fallback @@ -696,7 +750,7 @@ class _DateRangeSelectorState extends State { if (calendarModel.isHijri) { // For Hijri calendar final hijriCurrent = calendarModel.gregorianToHijri(currentDate); - final hijriDisplayed = calendarModel.gregorianToHijri(displayedDate); + final hijriDisplayed = _currentHijriDisplay ?? calendarModel.gregorianToHijri(displayedDate); currentMonth = hijriCurrent.month; currentYear = hijriCurrent.year; displayedYear = hijriDisplayed.year; @@ -730,11 +784,9 @@ class _DateRangeSelectorState extends State { try { final hijriDate = HijriGregDate(day: 1, month: monthIndex, year: displayedYear); final gregorianDate = calendarModel.hijriToGregorian(hijriDate); - _calendarController.displayDate = DateTime(gregorianDate.year, gregorianDate.month, 1); + _calendarController.displayDate = gregorianDate; // Track the Hijri month/year for accurate header display _currentHijriDisplay = hijriDate; - // Clear cache since we changed the month - _hijriCache.clear(); } catch (e) { // Fallback if conversion fails _calendarController.displayDate = DateTime(displayedDate.year, monthIndex, 1); @@ -784,7 +836,7 @@ class _DateRangeSelectorState extends State { if (calendarModel.isHijri) { // For Hijri calendar - final hijriDisplayed = calendarModel.gregorianToHijri(displayedDate); + final hijriDisplayed = _currentHijriDisplay ?? calendarModel.gregorianToHijri(displayedDate); final hijriCurrent = calendarModel.gregorianToHijri(DateTime.now()); displayedYear = hijriDisplayed.year; currentDisplayYear = hijriCurrent.year; @@ -818,7 +870,7 @@ class _DateRangeSelectorState extends State { final currentHijriMonth = _currentHijriDisplay?.month ?? calendarModel.gregorianToHijri(displayedDate).month; final hijriDate = HijriGregDate(day: 1, month: currentHijriMonth, year: year); final gregorianDate = calendarModel.hijriToGregorian(hijriDate); - _calendarController.displayDate = DateTime(gregorianDate.year, gregorianDate.month, 1); + _calendarController.displayDate = gregorianDate; // Track the Hijri month/year for accurate header display _currentHijriDisplay = hijriDate; } catch (e) { @@ -876,34 +928,34 @@ class _DateRangeSelectorState extends State { } displayDate(String label, String? date, bool isNotSelected) => Expanded( - child: Row( - spacing: 12.h, + child: Row( + spacing: 12.h, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.rangeCalendar, iconColor: isNotSelected ? AppColors.borderOnlyColor : AppColors.blackColor, height: 24, width: 24), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Utils.buildSvgWithAssets(icon: AppAssets.rangeCalendar, iconColor: isNotSelected ? AppColors.borderOnlyColor : AppColors.blackColor, height: 24, width: 24), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: TextStyle( - color: AppColors.inputLabelTextColor, - fontSize: 12.f, - fontWeight: FontWeight.w600, - ), - ), - Text( - date!, - style: TextStyle( - color: AppColors.textColor, - fontSize: 14.f, - fontWeight: FontWeight.w600, - ), - ) - ], + Text( + label, + style: TextStyle( + color: AppColors.inputLabelTextColor, + fontSize: 12.f, + fontWeight: FontWeight.w600, + ), + ), + Text( + date!, + style: TextStyle( + color: AppColors.textColor, + fontSize: 14.f, + fontWeight: FontWeight.w600, + ), ) ], - ), - ); + ) + ], + ), + ); selectionChip(DateRangeSelectorRangeViewModel model) { return Row( diff --git a/lib/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart b/lib/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart index 54c23a72..50b416cd 100644 --- a/lib/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart +++ b/lib/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart @@ -122,12 +122,12 @@ class HijriGregConverter { static int _hijriYearStartJulian(int hijriYear) { if (hijriYear <= 1) return _hijriEpoch; - int totalDays = 0; - for (int year = 1; year < hijriYear; year++) { - totalDays += _hijriYearLength(year); - } - - return _hijriEpoch + totalDays; + // A Hijri year has 354 days, with 11 leap days in every 30-year + // cycle. Calculate the completed years directly instead of iterating + // from year 1 for every calendar cell. + final completedYears = hijriYear - 1; + final completedLeapDays = (3 + (11 * hijriYear)) ~/ 30; + return _hijriEpoch + (completedYears * 354) + completedLeapDays; } static int _hijriYearLength(int year) { @@ -358,6 +358,8 @@ class DateRangCalenderModel extends ChangeNotifier { } void setTabIndex(int index) { + if (_selectedTabIndex == index) return; + _selectedTabIndex = index; _calendarType = index == 0 ? CalendarType.gregorian : CalendarType.hijri; // Persist the selection for next time the widget opens diff --git a/pubspec.yaml b/pubspec.yaml index 6ca96768..4c6b92b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -63,7 +63,7 @@ dependencies: geolocator: ^14.0.2 dropdown_search: ^6.0.2 google_maps_flutter: ^2.13.1 - # flutter_zoom_videosdk: 2.1.10 +# flutter_zoom_videosdk: 2.1.10 flutter_zoom_videosdk: ^2.5.10 dart_jsonwebtoken: ^3.2.0 dartz: ^0.10.1