From cc8c8707142c4f44cf3c34e337e4a3b563818356 Mon Sep 17 00:00:00 2001 From: Shubham Jitiya Date: Fri, 3 Jan 2025 18:03:45 +0530 Subject: [PATCH] =?UTF-8?q?:feat:=20Fixes=20issue=20#382:=20=F0=9F=9A=A7?= =?UTF-8?q?=20Added=20support=20for=203-Days=20week=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/lib/widgets/week_view_widget.dart | 4 + lib/src/extensions.dart | 84 ++++++---- .../week_view/_internal_week_view_page.dart | 5 + lib/src/week_view/week_view.dart | 147 ++++++++++++++---- 4 files changed, 187 insertions(+), 53 deletions(-) diff --git a/example/lib/widgets/week_view_widget.dart b/example/lib/widgets/week_view_widget.dart index 4222c531..0d231c7e 100644 --- a/example/lib/widgets/week_view_widget.dart +++ b/example/lib/widgets/week_view_widget.dart @@ -12,10 +12,14 @@ class WeekViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { return WeekView( + // TODO(Shubham): Fix min date not working + minDay: DateTime(2024, 1, 10), key: state, width: width, showWeekends: true, showLiveTimeLineInAllDays: true, + showThreeDaysView: true, + // startDay: WeekDays.tuesday, eventArranger: SideEventArranger(maxWidth: 30), timeLineWidth: 65, scrollPhysics: const BouncingScrollPhysics(), diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 7e6165d7..72401b8c 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -36,13 +36,26 @@ extension DateTimeExtensions on DateTime { .abs(); /// Gets difference of weeks between [date] and calling object. - int getWeekDifference(DateTime date, {WeekDays start = WeekDays.monday}) => - (firstDayOfWeek(start: start) - .difference(date.firstDayOfWeek(start: start)) - .inDays - .abs() / - 7) - .ceil(); + int getWeekDifference( + DateTime date, { + WeekDays start = WeekDays.monday, + bool isThreeDaysView = false, + }) { + final thisFirstDay = firstDayOfWeek( + start: start, + isThreeDaysView: isThreeDaysView, + ); + final otherFirstDay = date.firstDayOfWeek( + start: start, + isThreeDaysView: isThreeDaysView, + ); + + final daysDifference = thisFirstDay.difference(otherFirstDay).inDays.abs(); + + // Calculate the difference in weeks or 3-day blocks + final divisor = isThreeDaysView ? 3 : 7; + return (daysDifference / divisor).ceil(); + } /// Returns The List of date of Current Week, all of the dates will be without /// time. @@ -55,6 +68,7 @@ extension DateTimeExtensions on DateTime { List datesOfWeek({ WeekDays start = WeekDays.monday, bool showWeekEnds = true, + bool showThreeDays = false, }) { // Here %7 ensure that we do not subtract >6 and <0 days. // Initial formula is, @@ -63,31 +77,49 @@ extension DateTimeExtensions on DateTime { // But in WeekDays enum index ranges from 0 to 6 so we are // adding 1 in index. So, new formula with WeekDays is, // difference = (weekdays - (start.index + 1))%7 - // - final startDay = - DateTime(year, month, day - (weekday - start.index - 1) % 7); + // Generate weekdays with weekends or without weekends - final days = List.generate( - 7, - (index) => DateTime(startDay.year, startDay.month, startDay.day + index), - ) - .where( - (date) => - showWeekEnds || - (date.weekday != DateTime.saturday && - date.weekday != DateTime.sunday), - ) - .toList(); - return days; + + final days = showThreeDays ? day : day - (weekday - start.index - 1) % 7; + final startDay = DateTime(year, month, days); + // TODO(Shubham): Update for hide/show weekends + return List.generate( + showThreeDays ? 3 : 7, + (index) => startDay.add(Duration(days: index)), + ); } /// Returns the first date of week containing the current date - DateTime firstDayOfWeek({WeekDays start = WeekDays.monday}) => - DateTime(year, month, day - ((weekday - start.index - 1) % 7)); + DateTime firstDayOfWeek({ + WeekDays start = WeekDays.monday, + bool isThreeDaysView = false, + }) => + DateTime( + year, + month, + day - ((weekday - start.index - 1) % (isThreeDaysView ? 3 : 7)), + ); /// Returns the last date of week containing the current date - DateTime lastDayOfWeek({WeekDays start = WeekDays.monday}) => - DateTime(year, month, day + (6 - (weekday - start.index - 1) % 7)); + DateTime lastDayOfWeek({ + WeekDays start = WeekDays.monday, + bool isThreeDaysView = false, + }) { + if (isThreeDaysView) { + // Calculate the current weekday offset (based on the start day) + final offset = (weekday - start.index) % 7; + + // For a 3-day view, calculate the remaining days to the end of the 3-day block + final daysToAdd = + (3 - offset % 3) % 3; // Ensures wrap-around within 3-day blocks + final lastDay = DateTime(year, month, day + daysToAdd); + debugPrint('Last Day for 3-Day View: $lastDay'); + debugPrint('Last Day: ${lastDay}'); + return lastDay; + } else { + return DateTime(year, month, day + (6 - (weekday - start.index - 1) % 7)); + } + } /// Returns list of all dates of [month]. /// All the dates are week based that means it will return array of size 42 diff --git a/lib/src/week_view/_internal_week_view_page.dart b/lib/src/week_view/_internal_week_view_page.dart index 85d36b54..39013c23 100644 --- a/lib/src/week_view/_internal_week_view_page.dart +++ b/lib/src/week_view/_internal_week_view_page.dart @@ -139,6 +139,10 @@ class InternalWeekViewPage extends StatefulWidget { /// Flag to display quarter hours final bool showQuarterHours; + /// Enable this flag to show 3-days view default is false. + /// i.e 7 days view + final bool showThreeDaysView; + /// Emulate vertical line offset from hour line starts. final double emulateVerticalOffsetBy; @@ -205,6 +209,7 @@ class InternalWeekViewPage extends StatefulWidget { required this.showWeekDayAtBottom, required this.showHalfHours, required this.showQuarterHours, + required this.showThreeDaysView, required this.emulateVerticalOffsetBy, required this.onTileDoubleTap, required this.endHour, diff --git a/lib/src/week_view/week_view.dart b/lib/src/week_view/week_view.dart index b91efa13..d437f0e3 100644 --- a/lib/src/week_view/week_view.dart +++ b/lib/src/week_view/week_view.dart @@ -172,6 +172,10 @@ class WeekView extends StatefulWidget { /// saturday and sunday, only monday and tuesday will be visible in week view. final bool showWeekends; + /// Enable this flag to show 3-days view default is false. + /// i.e 7 days view + final bool showThreeDaysView; + /// Defines which days should be displayed in one week. /// /// By default all the days will be visible. @@ -287,6 +291,7 @@ class WeekView extends StatefulWidget { this.onDateTap, this.weekDays = WeekDays.values, this.showWeekends = true, + this.showThreeDaysView = false, this.startDay = WeekDays.monday, this.minuteSlotSize = MinuteSlotSize.minutes60, this.weekDetectorBuilder, @@ -333,6 +338,10 @@ class WeekView extends StatefulWidget { endHour <= Constants.hoursADay || endHour < startHour, "End hour must be less than 24 or startHour must be less than endHour", ), + assert(!(showThreeDaysView && !showWeekends), + "For three days view, showWeekends should be true"), + assert(!(showThreeDaysView && weekDays.length != 7), + "For three days view, weekDays should not be set"), super(key: key); @override @@ -411,6 +420,8 @@ class WeekViewState extends State> { _currentWeek = (widget.initialDay ?? DateTime.now()).withoutTime; _regulateCurrentDate(); + debugPrint('start date --> ${_currentStartDate}'); + debugPrint('end date --> ${_currentEndDate}'); _calculateHeights(); @@ -519,12 +530,15 @@ class WeekViewState extends State> { physics: widget.pageViewPhysics, onPageChanged: _onPageChange, itemBuilder: (_, index) { - final dates = DateTime(_minDate.year, _minDate.month, - _minDate.day + (index * DateTime.daysPerWeek)) - .datesOfWeek( - start: widget.startDay, - showWeekEnds: widget.showWeekends, - ); + // final dates = DateTime(_minDate.year, _minDate.month, + // _minDate.day + (index * DateTime.daysPerWeek)) + // .datesOfWeek( + // start: widget.startDay, + // showWeekEnds: widget.showWeekends, + // ); + + // TODO(Shubham): Three days view + final dates = _getDatesOnWeek(index); return ValueListenableBuilder( valueListenable: _scrollConfiguration, @@ -573,6 +587,7 @@ class WeekViewState extends State> { startHour: _startHour, showHalfHours: widget.showHalfHours, showQuarterHours: widget.showQuarterHours, + showThreeDaysView: widget.showThreeDaysView, emulateVerticalOffsetBy: widget.emulateVerticalOffsetBy, showWeekDayAtBottom: widget.showWeekDayAtBottom, @@ -631,7 +646,7 @@ class WeekViewState extends State> { "Make sure you are providing weekdays in initialization of " "WeekView. or showWeekends is true if you are providing only " "saturday or sunday in weekDays."); - _totalDaysInWeek = _weekDays.length; + _totalDaysInWeek = widget.showThreeDaysView ? 3 : _weekDays.length; } void _updateViewDimensions() { @@ -724,31 +739,58 @@ class WeekViewState extends State> { } else if (_currentWeek.isAfter(_maxDate)) { _currentWeek = _maxDate; } + _currentStartDate = _currentWeek.firstDayOfWeek( + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ); + + _currentEndDate = _currentWeek.lastDayOfWeek( + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ); - _currentStartDate = _currentWeek.firstDayOfWeek(start: widget.startDay); - _currentEndDate = _currentWeek.lastDayOfWeek(start: widget.startDay); - _currentIndex = - _minDate.getWeekDifference(_currentEndDate, start: widget.startDay); + _currentIndex = _minDate.getWeekDifference( + _currentEndDate, + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ); } /// Sets the minimum and maximum dates for current view. void _setDateRange() { _minDate = (widget.minDay ?? CalendarConstants.epochDate) - .firstDayOfWeek(start: widget.startDay) + .firstDayOfWeek( + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ) .withoutTime; + debugPrint('Min date weekview -->> ${_minDate}'); + _maxDate = (widget.maxDay ?? CalendarConstants.maxDate) - .lastDayOfWeek(start: widget.startDay) + .lastDayOfWeek( + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ) .withoutTime; + // if (widget.showThreeDaysView) { + // // _minDate.subtract(Duration(days: 3)); + // _maxDate = _maxDate.subtract(const Duration(days: 1)); + // } + debugPrint('MaxDate: ${_maxDate}'); assert( _minDate.isBefore(_maxDate), "Minimum date must be less than maximum date.\n" "Provided minimum date: $_minDate, maximum date: $_maxDate", ); - _totalWeeks = - _minDate.getWeekDifference(_maxDate, start: widget.startDay) + 1; + _totalWeeks = _minDate.getWeekDifference( + _maxDate, + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ) + + 1; } /// Default press detector builder. This builder will be used if @@ -790,6 +832,9 @@ class WeekViewState extends State> { /// Default builder for week number. Widget _defaultWeekNumberBuilder(DateTime date) { + if (widget.showThreeDaysView) { + return const SizedBox.shrink(); + } final daysToAdd = DateTime.thursday - date.weekday; final thursday = daysToAdd > 0 ? date.add(Duration(days: daysToAdd)) @@ -892,15 +937,24 @@ class WeekViewState extends State> { /// Called when user change page using any gesture or inbuilt functions. void _onPageChange(int index) { if (mounted) { - setState(() { - _currentStartDate = DateTime( - _currentStartDate.year, - _currentStartDate.month, - _currentStartDate.day + (index - _currentIndex) * 7, - ); - _currentEndDate = _currentStartDate.add(Duration(days: 6)); - _currentIndex = index; - }); + // Changes start date + _currentStartDate = DateTime( + _currentStartDate.year, + _currentStartDate.month, + // TODO(Shubham): Test this + _currentStartDate.day + + (index - _currentIndex) * (widget.showThreeDaysView ? 3 : 7), + ); + + // TODO(Shubham): Test this + // Add days for next page + _currentEndDate = _currentStartDate + .add(Duration(days: widget.showThreeDaysView ? 2 : 6)); + if (_currentStartDate.isBefore(_minDate)) { + _minDate = _currentStartDate; + } + _currentIndex = index; + setState(() {}); } widget.onPageChange?.call(_currentStartDate, _currentIndex); } @@ -911,6 +965,7 @@ class WeekViewState extends State> { /// as [DayView.pageTransitionDuration] and [DayView.pageTransitionCurve] /// respectively. void nextPage({Duration? duration, Curve? curve}) { + debugPrint('End date --->>> ${_currentEndDate}'); _pageController.nextPage( duration: duration ?? widget.pageTransitionDuration, curve: curve ?? widget.pageTransitionCurve, @@ -954,8 +1009,13 @@ class WeekViewState extends State> { if (week.isBefore(_minDate) || week.isAfter(_maxDate)) { throw "Invalid date selected."; } - _pageController - .jumpToPage(_minDate.getWeekDifference(week, start: widget.startDay)); + _pageController.jumpToPage( + _minDate.getWeekDifference( + week, + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ), + ); } /// Animate to page which gives day calendar for [week]. @@ -969,7 +1029,11 @@ class WeekViewState extends State> { throw "Invalid date selected."; } await _pageController.animateToPage( - _minDate.getWeekDifference(week, start: widget.startDay), + _minDate.getWeekDifference( + week, + start: widget.startDay, + isThreeDaysView: widget.showThreeDaysView, + ), duration: duration ?? widget.pageTransitionDuration, curve: curve ?? widget.pageTransitionCurve, ); @@ -1038,6 +1102,35 @@ class WeekViewState extends State> { void _scrollPageListener(ScrollController controller) { _lastScrollOffset = controller.offset; } + + List _getDatesOnWeek(int index) { + if (widget.showThreeDaysView) { + final days = _currentStartDate.datesOfWeek( + start: widget.startDay, + showThreeDays: widget.showThreeDaysView, + ); + // debugPrint('Generate days: ${days}'); + return days; + } else { + return DateTime( + _minDate.year, + _minDate.month, + _minDate.day + (index * DateTime.daysPerWeek), + ).datesOfWeek( + start: widget.startDay, + showThreeDays: widget.showThreeDaysView, + ); + } + // return DateTime( + // _minDate.year, + // _minDate.month, + // _minDate.day + + // (index * (widget.showThreeDaysView ? 3 : DateTime.daysPerWeek)), + // ).datesOfWeek( + // start: widget.startDay, + // showThreeDays: widget.showThreeDaysView, + // ); + } } class WeekHeader {