Skip to content

Commit

Permalink
feat: Fixes issue #385: Add showWeekends flag in month view
Browse files Browse the repository at this point in the history
  • Loading branch information
shubham-jitiya-simform committed Dec 3, 2024
1 parent cc99a0e commit b4d6faf
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 28 deletions.
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ List<CalendarEventData> _events = [
endTime: DateTime(_now.year, _now.month, _now.day, 22),
),
CalendarEventData(
date: _now.add(Duration(days: 1)),
date: _now.add(Duration(days: 2)),
startTime: DateTime(_now.year, _now.month, _now.day, 18),
endTime: DateTime(_now.year, _now.month, _now.day, 19),
title: "Wedding anniversary",
Expand Down
2 changes: 2 additions & 0 deletions example/lib/widgets/month_view_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class MonthViewWidget extends StatelessWidget {
return MonthView(
key: state,
width: width,
showWeekends: false,
useAvailableVerticalSpace: false,
hideDaysNotInMonth: false,
onEventTap: (event, date) {
Navigator.of(context).push(
Expand Down
23 changes: 22 additions & 1 deletion lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,30 @@ extension DateTimeExtensions on DateTime {
/// All the dates are week based that means it will return array of size 42
/// which will contain 6 weeks that is the maximum number of weeks a month
/// can have.
List<DateTime> datesOfMonths({WeekDays startDay = WeekDays.monday}) {
List<DateTime> datesOfMonths({
WeekDays startDay = WeekDays.monday,
bool hideDaysNotInMonth = false,
}) {
final monthDays = <DateTime>[];
// Friday's date for hidden weekends
final lastDateOfFirstWeek = firstDayOfWeek().add(const Duration(days: 4));
// Start is the first weekday for each week in a month
for (var i = 1, start = 1; i < 7; i++, start += 7) {
// Any date of week is in current month
final anyDateInCurrentMonth = DateTime(year, month, start)
.datesOfWeek(start: startDay)
.any((date) => date.month == month);

if (hideDaysNotInMonth) {
// Check if first row need to skip
if (i == 1 && lastDateOfFirstWeek.month != month) {
continue;
}
// Check if last row need to skip
if (i == 6 && !anyDateInCurrentMonth) {
continue;
}
}
monthDays
.addAll(DateTime(year, month, start).datesOfWeek(start: startDay));
}
Expand Down
131 changes: 105 additions & 26 deletions lib/src/month_view/month_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ class MonthView<T extends Object?> extends StatefulWidget {
/// This method will be called when user double taps on event tile.
final TileTapCallback<T>? onEventDoubleTap;

/// Show weekends or not
///
/// Default value is true.
final bool showWeekends;

/// Builds the name of the weeks.
///
/// Used default week builder if null.
Expand Down Expand Up @@ -184,6 +189,7 @@ class MonthView<T extends Object?> extends StatefulWidget {
this.maxMonth,
this.controller,
this.initialMonth,
this.showWeekends = true,
this.borderSize = 1,
this.useAvailableVerticalSpace = false,
this.cellAspectRatio = 0.55,
Expand Down Expand Up @@ -352,7 +358,7 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
width: _width,
child: Row(
children: List.generate(
7,
widget.showWeekends ? 7 : 5,
(index) => Expanded(
child: SizedBox(
width: _cellWidth,
Expand All @@ -365,12 +371,15 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
),
Expanded(
child: LayoutBuilder(builder: (context, constraints) {
final _cellAspectRatio =
widget.useAvailableVerticalSpace
? calculateCellAspectRatio(
constraints.maxHeight,
)
: widget.cellAspectRatio;
final _cellAspectRatio = widget
.useAvailableVerticalSpace
? calculateCellAspectRatio(
height: constraints.maxHeight,
date: date,
startDay: widget.startDay,
hideDaysNotInMonth: widget.hideDaysNotInMonth,
)
: widget.cellAspectRatio;

return SizedBox(
height: _height,
Expand All @@ -391,6 +400,7 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
startDay: widget.startDay,
physics: widget.pagePhysics,
hideDaysNotInMonth: widget.hideDaysNotInMonth,
weekDays: widget.showWeekends ? 7 : 5,
),
);
}),
Expand Down Expand Up @@ -432,8 +442,21 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
_height = _cellHeight * 6;
}

double calculateCellAspectRatio(double height) {
final _cellHeight = height / 6;
double calculateCellAspectRatio({
required double height,
required DateTime date,
required WeekDays startDay,
required hideDaysNotInMonth,
}) {
// Required rows
final daysInMonth = date
.datesOfMonths(
startDay: startDay,
hideDaysNotInMonth: hideDaysNotInMonth,
)
.length /
7;
final _cellHeight = height / daysInMonth;
return _cellWidth / _cellHeight;
}

Expand Down Expand Up @@ -663,6 +686,7 @@ class _MonthPageBuilder<T> extends StatelessWidget {
final WeekDays startDay;
final ScrollPhysics physics;
final bool hideDaysNotInMonth;
final int weekDays;

const _MonthPageBuilder({
Key? key,
Expand All @@ -680,30 +704,32 @@ class _MonthPageBuilder<T> extends StatelessWidget {
required this.startDay,
required this.physics,
required this.hideDaysNotInMonth,
required this.weekDays,
}) : super(key: key);

@override
Widget build(BuildContext context) {
final monthDays = date.datesOfMonths(startDay: startDay);
return Container(
width: width,
height: height,
child: GridView.builder(
padding: EdgeInsets.zero,
physics: physics,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
childAspectRatio: cellRatio,
),
itemCount: 42,
shrinkWrap: true,
itemBuilder: (context, index) {
final events = controller.getEventsOnDay(monthDays[index]);
return GestureDetector(
// There can we 20 days or 25 days
// Update start day
final monthDays = date.datesOfMonths(
startDay: startDay, hideDaysNotInMonth: hideDaysNotInMonth);
final maxCells = _getMaxCells(monthDays.length);
final gridViewItems = <Widget>[];
for (var index = 0; index < monthDays.length; index++) {
final events = controller.getEventsOnDay(monthDays[index]);
bool isWeekend = monthDays[index].weekday == DateTime.saturday ||
monthDays[index].weekday == DateTime.sunday;
if (isWeekend && weekDays == 5) {
continue;
}
if (!isWeekend || isWeekend && weekDays == 7) {
gridViewItems.add(
GestureDetector(
onTap: () => onCellTap?.call(events, monthDays[index]),
onLongPress: () => onDateLongPress?.call(monthDays[index]),
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
border: showBorder
? Border.all(
color: borderColor,
Expand All @@ -719,11 +745,64 @@ class _MonthPageBuilder<T> extends StatelessWidget {
hideDaysNotInMonth,
),
),
);
),
);
}
}

// Highlight tiles which is not in current month
return Container(
width: width,
height: height,
child: GridView.builder(
padding: EdgeInsets.zero,
physics: physics,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: weekDays,
childAspectRatio: cellRatio,
),
itemCount: maxCells,
shrinkWrap: true,
itemBuilder: (context, index) {
if (index > gridViewItems.length - 1) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[200],
border: showBorder
? Border.all(
color: borderColor,
width: borderSize,
)
: null,
));
}
return gridViewItems[index];
},
),
);
}

int _getMaxCells(int daysInMonth) {
// Show weekends - true
if (weekDays == 7) {
// Show days not in month
if (!hideDaysNotInMonth) {
return 42;
}
return daysInMonth <= 28 ? 28 : 35;
} else {
// Hide weekends and show days not in month
if (!hideDaysNotInMonth) {
// 6 weeks: 5 week days * 6 weeks = 30
return 30;
} else {
// When weekends are hidden & hide days not in month is true
// If daysInMonth is less than 28 max rows we can have is 4 otherwise 5.
// Weekends are included in daysInMonth
return daysInMonth <= 28 ? 20 : 25;
}
}
}
}

class MonthHeader {
Expand Down

0 comments on commit b4d6faf

Please sign in to comment.