From 533ac776be90a6536094d35b6b4a54475c325364 Mon Sep 17 00:00:00 2001 From: Shubham Jitiya Date: Fri, 29 Nov 2024 15:53:09 +0530 Subject: [PATCH] feat: Fixes issue #385: Add `showWeekends` flag in month view --- example/ios/Runner/AppDelegate.swift | 2 +- example/lib/main.dart | 2 +- example/lib/widgets/month_view_widget.dart | 2 + lib/src/extensions.dart | 21 +++- lib/src/month_view/month_view.dart | 131 +++++++++++++++++---- 5 files changed, 129 insertions(+), 29 deletions(-) diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4a..b6363034 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/example/lib/main.dart b/example/lib/main.dart index 146e23af..4735cb2a 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -43,7 +43,7 @@ List _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", diff --git a/example/lib/widgets/month_view_widget.dart b/example/lib/widgets/month_view_widget.dart index bacec7eb..4eade8a3 100644 --- a/example/lib/widgets/month_view_widget.dart +++ b/example/lib/widgets/month_view_widget.dart @@ -18,6 +18,8 @@ class MonthViewWidget extends StatelessWidget { return MonthView( key: state, width: width, + showWeekends: false, + useAvailableVerticalSpace: true, hideDaysNotInMonth: false, onEventTap: (event, date) { Navigator.of(context).push( diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index 36077fd0..44384fbe 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -87,9 +87,28 @@ 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 datesOfMonths({WeekDays startDay = WeekDays.monday}) { + List datesOfMonths({ + WeekDays startDay = WeekDays.monday, + bool hideDaysNotInMonth = false, + }) { final monthDays = []; + // Start is the first weekday for each week in a month + // If showWeekends is false, max rows we need to generate is 5. + final lastDateOfFirstWeek = firstDayOfWeek().add(Duration(days: 4)); for (var i = 1, start = 1; i < 7; i++, start += 7) { + // Update last date: Friday's date + // No any date is in current month + final anyDateInCurrentMonth = DateTime(year, month, start) + .datesOfWeek(start: startDay) + .any((date) => date.month == month); + if (hideDaysNotInMonth && + start == 1 && + lastDateOfFirstWeek.month != month) { + continue; + } + if (hideDaysNotInMonth && i == 6 && !anyDateInCurrentMonth) { + continue; + } monthDays .addAll(DateTime(year, month, start).datesOfWeek(start: startDay)); } diff --git a/lib/src/month_view/month_view.dart b/lib/src/month_view/month_view.dart index 51690910..4828ebd8 100644 --- a/lib/src/month_view/month_view.dart +++ b/lib/src/month_view/month_view.dart @@ -64,6 +64,11 @@ class MonthView extends StatefulWidget { /// This method will be called when user double taps on event tile. final TileTapCallback? onEventDoubleTap; + /// Show weekends or not + /// + /// Default value is true. + final bool showWeekends; + /// Builds the name of the weeks. /// /// Used default week builder if null. @@ -184,6 +189,7 @@ class MonthView extends StatefulWidget { this.maxMonth, this.controller, this.initialMonth, + this.showWeekends = true, this.borderSize = 1, this.useAvailableVerticalSpace = false, this.cellAspectRatio = 0.55, @@ -352,7 +358,7 @@ class MonthViewState extends State> { width: _width, child: Row( children: List.generate( - 7, + widget.showWeekends ? 7 : 5, (index) => Expanded( child: SizedBox( width: _cellWidth, @@ -365,12 +371,15 @@ class MonthViewState extends State> { ), 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, @@ -391,6 +400,7 @@ class MonthViewState extends State> { startDay: widget.startDay, physics: widget.pagePhysics, hideDaysNotInMonth: widget.hideDaysNotInMonth, + weekDays: widget.showWeekends ? 7 : 5, ), ); }), @@ -432,8 +442,21 @@ class MonthViewState extends State> { _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; } @@ -663,6 +686,7 @@ class _MonthPageBuilder extends StatelessWidget { final WeekDays startDay; final ScrollPhysics physics; final bool hideDaysNotInMonth; + final int weekDays; const _MonthPageBuilder({ Key? key, @@ -680,30 +704,32 @@ class _MonthPageBuilder 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 = []; + 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, @@ -719,11 +745,64 @@ class _MonthPageBuilder 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 {