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 8d44035
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 25 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
4 changes: 3 additions & 1 deletion example/lib/widgets/month_view_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class MonthViewWidget extends StatelessWidget {
return MonthView(
key: state,
width: width,
hideDaysNotInMonth: false,
showWeekends: false,
useAvailableVerticalSpace: true,
hideDaysNotInMonth: true,
onEventTap: (event, date) {
Navigator.of(context).push(
MaterialPageRoute(
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
103 changes: 81 additions & 22 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,10 +371,15 @@ class MonthViewState<T extends Object?> extends State<MonthView<T>> {
),
Expanded(
child: LayoutBuilder(builder: (context, constraints) {
final datesInMonth = date.datesOfMonths(
startDay: widget.startDay,
hideDaysNotInMonth: widget.hideDaysNotInMonth,
);
final _cellAspectRatio =
widget.useAvailableVerticalSpace
? calculateCellAspectRatio(
constraints.maxHeight,
height: constraints.maxHeight,
daysInMonth: datesInMonth.length,
)
: widget.cellAspectRatio;

Expand All @@ -391,6 +402,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 +444,12 @@ 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 int daysInMonth,
}) {
final rows = daysInMonth / 7;
final _cellHeight = height / rows;
return _cellWidth / _cellHeight;
}

Expand Down Expand Up @@ -663,6 +679,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 +697,33 @@ 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]);
final 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 +739,50 @@ class _MonthPageBuilder<T> extends StatelessWidget {
hideDaysNotInMonth,
),
),
);
},
),
);
}
}

// Highlight tiles which is not in current month
return SizedBox(
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) => 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 8d44035

Please sign in to comment.