Skip to content

Commit

Permalink
:feat: Fixes issue #382: 🚧 Added support for 3-Days week view
Browse files Browse the repository at this point in the history
shubham-jitiya-simform committed Jan 8, 2025
1 parent 469cc29 commit cc8c870
Showing 4 changed files with 187 additions and 53 deletions.
4 changes: 4 additions & 0 deletions example/lib/widgets/week_view_widget.dart
Original file line number Diff line number Diff line change
@@ -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(),
84 changes: 58 additions & 26 deletions lib/src/extensions.dart
Original file line number Diff line number Diff line change
@@ -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<DateTime> 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
5 changes: 5 additions & 0 deletions lib/src/week_view/_internal_week_view_page.dart
Original file line number Diff line number Diff line change
@@ -139,6 +139,10 @@ class InternalWeekViewPage<T extends Object?> 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<T extends Object?> extends StatefulWidget {
required this.showWeekDayAtBottom,
required this.showHalfHours,
required this.showQuarterHours,
required this.showThreeDaysView,
required this.emulateVerticalOffsetBy,
required this.onTileDoubleTap,
required this.endHour,
147 changes: 120 additions & 27 deletions lib/src/week_view/week_view.dart
Original file line number Diff line number Diff line change
@@ -172,6 +172,10 @@ class WeekView<T extends Object?> 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<T extends Object?> 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<T extends Object?> 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<T extends Object?> extends State<WeekView<T>> {
_currentWeek = (widget.initialDay ?? DateTime.now()).withoutTime;

_regulateCurrentDate();
debugPrint('start date --> ${_currentStartDate}');
debugPrint('end date --> ${_currentEndDate}');

_calculateHeights();

@@ -519,12 +530,15 @@ class WeekViewState<T extends Object?> extends State<WeekView<T>> {
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<T extends Object?> extends State<WeekView<T>> {
startHour: _startHour,
showHalfHours: widget.showHalfHours,
showQuarterHours: widget.showQuarterHours,
showThreeDaysView: widget.showThreeDaysView,
emulateVerticalOffsetBy:
widget.emulateVerticalOffsetBy,
showWeekDayAtBottom: widget.showWeekDayAtBottom,
@@ -631,7 +646,7 @@ class WeekViewState<T extends Object?> extends State<WeekView<T>> {
"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<T extends Object?> extends State<WeekView<T>> {
} 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<T extends Object?> extends State<WeekView<T>> {

/// 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<T extends Object?> extends State<WeekView<T>> {
/// 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<T extends Object?> extends State<WeekView<T>> {
/// 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<T extends Object?> extends State<WeekView<T>> {
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<T extends Object?> extends State<WeekView<T>> {
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<T extends Object?> extends State<WeekView<T>> {
void _scrollPageListener(ScrollController controller) {
_lastScrollOffset = controller.offset;
}

List<DateTime> _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 {

0 comments on commit cc8c870

Please sign in to comment.