From 534ed79a6a7cd07816431b1391b9a95cb6f6e41b Mon Sep 17 00:00:00 2001 From: Jaimin Rana Date: Wed, 8 Nov 2023 11:07:19 +0530 Subject: [PATCH] feat: Added the quarterHourIndicator for the DayView & halfHourIndicator and quarterHourIndicator for WeekView #270. --- CHANGELOG.md | 6 +- lib/src/components/_internal_components.dart | 26 ++++ lib/src/day_view/_internal_day_view_page.dart | 29 +++++ lib/src/day_view/day_view.dart | 33 +++++ lib/src/painters.dart | 81 +++++++++++- lib/src/typedefs.dart | 1 + .../week_view/_internal_week_view_page.dart | 120 +++++++++++++----- lib/src/week_view/week_view.dart | 59 ++++++++- 8 files changed, 317 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index babf1a52..da983e09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,11 @@ view-[#269](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/269) - Added feature added a callback for the default header title - - [#241](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/241) + - [#241](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/241) +- Added + feature added the quarterHourIndicator for the DayView & halfHourIndicator and + quarterHourIndicator for WeekView + - [#270](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/issues/270) # [1.0.4 - 9 Aug 2023](https://github.com/SimformSolutionsPvtLtd/flutter_calendar_view/tree/1.0.4) diff --git a/lib/src/components/_internal_components.dart b/lib/src/components/_internal_components.dart index 1c647188..7efbb370 100644 --- a/lib/src/components/_internal_components.dart +++ b/lib/src/components/_internal_components.dart @@ -115,6 +115,9 @@ class TimeLine extends StatelessWidget { /// Flag to display half hours. final bool showHalfHours; + /// Flag to display quarter hours. + final bool showQuarterHours; + static DateTime get _date => DateTime.now(); double get _halfHourHeight => hourHeight / 2; @@ -128,6 +131,7 @@ class TimeLine extends StatelessWidget { required this.timeLineOffset, required this.timeLineBuilder, this.showHalfHours = false, + this.showQuarterHours = false, }) : super(key: key); @override @@ -157,6 +161,28 @@ class TimeLine extends StatelessWidget { hour: i, minutes: 30, ), + if (showQuarterHours) + for (int i = 0; i < Constants.hoursADay; i++) ...[ + /// this is for 15 minutes + _timelinePositioned( + topPosition: + hourHeight * i - timeLineOffset + hourHeight * 0.25, + bottomPosition: + height - (hourHeight * (i + 1)) + timeLineOffset, + hour: i, + minutes: 15, + ), + + /// this is for 45 minutes + _timelinePositioned( + topPosition: + hourHeight * i - timeLineOffset + hourHeight * 0.75, + bottomPosition: + height - (hourHeight * (i + 1)) + timeLineOffset, + hour: i, + minutes: 45, + ), + ], ], ), ); diff --git a/lib/src/day_view/_internal_day_view_page.dart b/lib/src/day_view/_internal_day_view_page.dart index 29fe183f..00d32ccf 100644 --- a/lib/src/day_view/_internal_day_view_page.dart +++ b/lib/src/day_view/_internal_day_view_page.dart @@ -98,11 +98,20 @@ class InternalDayViewPage extends StatelessWidget { /// Flag to display half hours. final bool showHalfHours; + /// Flag to display quarter hours. + final bool showQuarterHours; + /// Settings for half hour indicator lines. final HourIndicatorSettings halfHourIndicatorSettings; + /// Settings for half hour indicator lines. + final HourIndicatorSettings quarterHourIndicatorSettings; + final ScrollController scrollController; + /// Emulate vertical line offset from hour line starts. + final double emulateVerticalOffsetBy; + /// Defines a single day page. const InternalDayViewPage({ Key? key, @@ -132,7 +141,10 @@ class InternalDayViewPage extends StatelessWidget { required this.scrollController, required this.dayDetectorBuilder, required this.showHalfHours, + required this.showQuarterHours, required this.halfHourIndicatorSettings, + required this.quarterHourIndicatorSettings, + required this.emulateVerticalOffsetBy, }) : super(key: key); @override @@ -166,6 +178,7 @@ class InternalDayViewPage extends StatelessWidget { hourIndicatorSettings.lineStyle, hourIndicatorSettings.dashWidth, hourIndicatorSettings.dashSpaceWidth, + emulateVerticalOffsetBy, ), ), if (showHalfHours) @@ -183,6 +196,21 @@ class InternalDayViewPage extends StatelessWidget { halfHourIndicatorSettings.dashSpaceWidth, ), ), + if (showQuarterHours) + CustomPaint( + size: Size(width, height), + painter: QuarterHourLinePainter( + lineColor: quarterHourIndicatorSettings.color, + lineHeight: quarterHourIndicatorSettings.height, + offset: timeLineWidth + + quarterHourIndicatorSettings.offset, + minuteHeight: heightPerMinute, + lineStyle: quarterHourIndicatorSettings.lineStyle, + dashWidth: quarterHourIndicatorSettings.dashWidth, + dashSpaceWidth: + quarterHourIndicatorSettings.dashSpaceWidth, + ), + ), dayDetectorBuilder( width: width, height: height, @@ -214,6 +242,7 @@ class InternalDayViewPage extends StatelessWidget { timeLineOffset: timeLineOffset, timeLineWidth: timeLineWidth, showHalfHours: showHalfHours, + showQuarterHours: showQuarterHours, key: ValueKey(heightPerMinute), ), if (showLiveLine && liveTimeIndicatorSettings.height > 0) diff --git a/lib/src/day_view/day_view.dart b/lib/src/day_view/day_view.dart index 54dec239..2430f809 100644 --- a/lib/src/day_view/day_view.dart +++ b/lib/src/day_view/day_view.dart @@ -95,6 +95,11 @@ class DayView extends StatefulWidget { /// Pass [HourIndicatorSettings.none] to remove half hour lines. final HourIndicatorSettings? halfHourIndicatorSettings; + /// Defines settings for quarter hour indication lines. + /// + /// Pass [HourIndicatorSettings.none] to remove quarter hour lines. + final HourIndicatorSettings? quarterHourIndicatorSettings; + /// Page transition duration used when user try to change page using /// [DayViewState.nextPage] or [DayViewState.previousPage] final Duration pageTransitionDuration; @@ -192,8 +197,12 @@ class DayView extends StatefulWidget { /// Display full day event builder. final FullDayEventBuilder? fullDayEventBuilder; + /// Show half hour indicator final bool showHalfHours; + /// Show quarter hour indicator(15min & 45min). + final bool showQuarterHours; + /// It define the starting duration from where day view page will be visible /// By default it will be Duration(hours:0) final Duration startDuration; @@ -201,6 +210,9 @@ class DayView extends StatefulWidget { /// Callback for the Header title final HeaderTitleCallback? onHeaderTitleTap; + /// Emulate vertical line offset from hour line starts. + final double emulateVerticalOffsetBy; + /// Main widget for day view. const DayView({ Key? key, @@ -240,9 +252,12 @@ class DayView extends StatefulWidget { this.pageViewPhysics, this.dayDetectorBuilder, this.showHalfHours = false, + this.showQuarterHours = false, this.halfHourIndicatorSettings, + this.quarterHourIndicatorSettings, this.startDuration = const Duration(hours: 0), this.onHeaderTitleTap, + this.emulateVerticalOffsetBy = 0, }) : assert(!(onHeaderTitleTap != null && dayTitleBuilder != null), "can't use [onHeaderTitleTap] & [dayTitleBuilder] simultaneously"), assert(timeLineOffset >= 0, @@ -279,6 +294,7 @@ class DayViewState extends State> { late HourIndicatorSettings _hourIndicatorSettings; late HourIndicatorSettings _halfHourIndicatorSettings; + late HourIndicatorSettings _quarterHourIndicatorSettings; late CustomHourLinePainter _hourLinePainter; late HourIndicatorSettings _liveTimeIndicatorSettings; @@ -443,8 +459,13 @@ class DayViewState extends State> { fullDayEventBuilder: _fullDayEventBuilder, scrollController: _scrollController, showHalfHours: widget.showHalfHours, + showQuarterHours: widget.showQuarterHours, halfHourIndicatorSettings: _halfHourIndicatorSettings, + quarterHourIndicatorSettings: + _quarterHourIndicatorSettings, + emulateVerticalOffsetBy: + widget.emulateVerticalOffsetBy, ), ); }, @@ -512,6 +533,16 @@ class DayViewState extends State> { assert(_halfHourIndicatorSettings.height < _hourHeight, "halfHourIndicator height must be less than minuteHeight * 60"); + + _quarterHourIndicatorSettings = widget.quarterHourIndicatorSettings ?? + HourIndicatorSettings( + height: widget.heightPerMinute, + color: Constants.defaultBorderColor, + offset: 5, + ); + + assert(_quarterHourIndicatorSettings.height < _hourHeight, + "quarterHourIndicator height must be less than minuteHeight * 60"); } void _calculateHeights() { @@ -688,6 +719,7 @@ class DayViewState extends State> { LineStyle lineStyle, double dashWidth, double dashSpaceWidth, + double emulateVerticalOffsetBy, ) { return HourLinePainter( lineColor: lineColor, @@ -699,6 +731,7 @@ class DayViewState extends State> { lineStyle: lineStyle, dashWidth: dashWidth, dashSpaceWidth: dashSpaceWidth, + emulateVerticalOffsetBy: emulateVerticalOffsetBy, ); } diff --git a/lib/src/painters.dart b/lib/src/painters.dart index 1c2a539b..e607a81c 100644 --- a/lib/src/painters.dart +++ b/lib/src/painters.dart @@ -36,6 +36,9 @@ class HourLinePainter extends CustomPainter { /// Line dash space width when using the [LineStyle.dashed] style final double dashSpaceWidth; + /// Emulates offset of vertical line from hour line starts. + final double emulateVerticalOffsetBy; + /// Paints 24 hour lines. HourLinePainter({ required this.lineColor, @@ -43,6 +46,7 @@ class HourLinePainter extends CustomPainter { required this.minuteHeight, required this.offset, required this.showVerticalLine, + required this.emulateVerticalOffsetBy, this.verticalLineOffset = 10, this.lineStyle = LineStyle.solid, this.dashWidth = 4, @@ -51,6 +55,7 @@ class HourLinePainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { + final dx = offset + emulateVerticalOffsetBy; final paint = Paint() ..color = lineColor ..strokeWidth = lineHeight; @@ -58,14 +63,14 @@ class HourLinePainter extends CustomPainter { for (var i = 1; i < Constants.hoursADay; i++) { final dy = i * minuteHeight * 60; if (lineStyle == LineStyle.dashed) { - var startX = offset; + var startX = dx; while (startX < size.width) { canvas.drawLine( Offset(startX, dy), Offset(startX + dashWidth, dy), paint); startX += dashWidth + dashSpaceWidth; } } else { - canvas.drawLine(Offset(offset, dy), Offset(size.width, dy), paint); + canvas.drawLine(Offset(dx, dy), Offset(size.width, dy), paint); } } @@ -157,6 +162,78 @@ class HalfHourLinePainter extends CustomPainter { } } +//using HalfHourIndicatorSettings for this too +class QuarterHourLinePainter extends CustomPainter { + /// Color of quarter hour line + final Color lineColor; + + /// Height of quarter hour line + final double lineHeight; + + /// Offset of quarter hour line from left. + final double offset; + + /// Height occupied by one minute of time stamp. + final double minuteHeight; + + /// Style of the quarter hour line + final LineStyle lineStyle; + + /// Line dash width when using the [LineStyle.dashed] style + final double dashWidth; + + /// Line dash space width when using the [LineStyle.dashed] style + final double dashSpaceWidth; + + /// Paint quarter hour lines + QuarterHourLinePainter({ + required this.lineColor, + required this.lineHeight, + required this.offset, + required this.minuteHeight, + required this.lineStyle, + this.dashWidth = 4, + this.dashSpaceWidth = 4, + }); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = lineColor + ..strokeWidth = lineHeight; + + for (var i = 0; i < Constants.hoursADay; i++) { + final dy1 = i * minuteHeight * 60 + (minuteHeight * 15); + final dy2 = i * minuteHeight * 60 + (minuteHeight * 45); + + if (lineStyle == LineStyle.dashed) { + var startX = offset; + while (startX < size.width) { + canvas.drawLine( + Offset(startX, dy1), Offset(startX + dashWidth, dy1), paint); + startX += dashWidth + dashSpaceWidth; + + canvas.drawLine( + Offset(startX, dy2), Offset(startX + dashWidth, dy2), paint); + startX += dashWidth + dashSpaceWidth; + } + } else { + canvas.drawLine(Offset(offset, dy1), Offset(size.width, dy1), paint); + canvas.drawLine(Offset(offset, dy2), Offset(size.width, dy2), paint); + } + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return oldDelegate is HourLinePainter && + (oldDelegate.lineColor != lineColor || + oldDelegate.offset != offset || + lineHeight != oldDelegate.lineHeight || + minuteHeight != oldDelegate.minuteHeight); + } +} + /// Paints a single horizontal line at [offset]. class CurrentTimeLinePainter extends CustomPainter { /// Color of time indicator. diff --git a/lib/src/typedefs.dart b/lib/src/typedefs.dart index cf0d6f14..61e72021 100644 --- a/lib/src/typedefs.dart +++ b/lib/src/typedefs.dart @@ -82,4 +82,5 @@ typedef CustomHourLinePainter = CustomPainter Function( LineStyle lineStyle, double dashWidth, double dashSpaceWidth, + double emulateVerticalOffsetBy, ); diff --git a/lib/src/week_view/_internal_week_view_page.dart b/lib/src/week_view/_internal_week_view_page.dart index b0113231..b8bfd1c0 100644 --- a/lib/src/week_view/_internal_week_view_page.dart +++ b/lib/src/week_view/_internal_week_view_page.dart @@ -10,6 +10,7 @@ import '../enumerations.dart'; import '../event_arrangers/event_arrangers.dart'; import '../event_controller.dart'; import '../modals.dart'; +import '../painters.dart'; import '../typedefs.dart'; /// A single page for week view. @@ -39,6 +40,12 @@ class InternalWeekViewPage extends StatelessWidget { /// Custom painter for hour line. final CustomHourLinePainter hourLinePainter; + /// Settings for half hour indicator lines. + final HourIndicatorSettings halfHourIndicatorSettings; + + /// Settings for quarter hour indicator lines. + final HourIndicatorSettings quarterHourIndicatorSettings; + /// Flag to display live line. final bool showLiveLine; @@ -113,40 +120,54 @@ class InternalWeekViewPage extends StatelessWidget { /// Display full day events. final FullDayEventBuilder fullDayEventBuilder; + /// Flag to display half hours + final bool showHalfHours; + + /// Flag to display quarter hours + final bool showQuarterHours; + + /// Emulate vertical line offset from hour line starts. + final double emulateVerticalOffsetBy; + /// A single page for week view. - const InternalWeekViewPage({ - Key? key, - required this.showVerticalLine, - required this.weekTitleHeight, - required this.weekDayBuilder, - required this.weekNumberBuilder, - required this.width, - required this.dates, - required this.eventTileBuilder, - required this.controller, - required this.timeLineBuilder, - required this.hourIndicatorSettings, - required this.hourLinePainter, - required this.showLiveLine, - required this.liveTimeIndicatorSettings, - required this.heightPerMinute, - required this.timeLineWidth, - required this.timeLineOffset, - required this.height, - required this.hourHeight, - required this.eventArranger, - required this.verticalLineOffset, - required this.weekTitleWidth, - required this.scrollController, - required this.onTileTap, - required this.onDateLongPress, - required this.onDateTap, - required this.weekDays, - required this.minuteSlotSize, - required this.scrollConfiguration, - required this.fullDayEventBuilder, - required this.weekDetectorBuilder, - }) : super(key: key); + const InternalWeekViewPage( + {Key? key, + required this.showVerticalLine, + required this.weekTitleHeight, + required this.weekDayBuilder, + required this.weekNumberBuilder, + required this.width, + required this.dates, + required this.eventTileBuilder, + required this.controller, + required this.timeLineBuilder, + required this.hourIndicatorSettings, + required this.hourLinePainter, + required this.halfHourIndicatorSettings, + required this.quarterHourIndicatorSettings, + required this.showLiveLine, + required this.liveTimeIndicatorSettings, + required this.heightPerMinute, + required this.timeLineWidth, + required this.timeLineOffset, + required this.height, + required this.hourHeight, + required this.eventArranger, + required this.verticalLineOffset, + required this.weekTitleWidth, + required this.scrollController, + required this.onTileTap, + required this.onDateLongPress, + required this.onDateTap, + required this.weekDays, + required this.minuteSlotSize, + required this.scrollConfiguration, + required this.fullDayEventBuilder, + required this.weekDetectorBuilder, + required this.showHalfHours, + required this.showQuarterHours, + required this.emulateVerticalOffsetBy}) + : super(key: key); @override Widget build(BuildContext context) { @@ -229,8 +250,39 @@ class InternalWeekViewPage extends StatelessWidget { hourIndicatorSettings.lineStyle, hourIndicatorSettings.dashWidth, hourIndicatorSettings.dashSpaceWidth, + emulateVerticalOffsetBy, ), ), + if (showHalfHours) + CustomPaint( + size: Size(width, height), + painter: HalfHourLinePainter( + lineColor: halfHourIndicatorSettings.color, + lineHeight: halfHourIndicatorSettings.height, + offset: + timeLineWidth + halfHourIndicatorSettings.offset, + minuteHeight: heightPerMinute, + lineStyle: halfHourIndicatorSettings.lineStyle, + dashWidth: halfHourIndicatorSettings.dashWidth, + dashSpaceWidth: + halfHourIndicatorSettings.dashSpaceWidth, + ), + ), + if (showQuarterHours) + CustomPaint( + size: Size(width, height), + painter: QuarterHourLinePainter( + lineColor: quarterHourIndicatorSettings.color, + lineHeight: quarterHourIndicatorSettings.height, + offset: timeLineWidth + + quarterHourIndicatorSettings.offset, + minuteHeight: heightPerMinute, + lineStyle: quarterHourIndicatorSettings.lineStyle, + dashWidth: quarterHourIndicatorSettings.dashWidth, + dashSpaceWidth: + quarterHourIndicatorSettings.dashSpaceWidth, + ), + ), if (showLiveLine && liveTimeIndicatorSettings.height > 0) LiveTimeIndicator( liveTimeIndicatorSettings: liveTimeIndicatorSettings, @@ -296,6 +348,8 @@ class InternalWeekViewPage extends StatelessWidget { height: height, timeLineOffset: timeLineOffset, timeLineBuilder: timeLineBuilder, + showHalfHours: showHalfHours, + showQuarterHours: showQuarterHours, ), ], ), diff --git a/lib/src/week_view/week_view.dart b/lib/src/week_view/week_view.dart index 82e90d98..0536d70a 100644 --- a/lib/src/week_view/week_view.dart +++ b/lib/src/week_view/week_view.dart @@ -91,6 +91,12 @@ class WeekView extends StatefulWidget { /// Use this if you want to paint custom hour lines. final CustomHourLinePainter? hourLinePainter; + /// Settings for half hour indicator settings. + final HourIndicatorSettings? halfHourIndicatorSettings; + + /// Settings for quarter hour indicator settings. + final HourIndicatorSettings? quarterHourIndicatorSettings; + /// Settings for live time indicator settings. final HourIndicatorSettings? liveTimeIndicatorSettings; @@ -192,6 +198,15 @@ class WeekView extends StatefulWidget { /// Display full day event builder. final FullDayEventBuilder? fullDayEventBuilder; + ///Show half hour indicator + final bool showHalfHours; + + ///Show quarter hour indicator + final bool showQuarterHours; + + ///Emulates offset of vertical line from hour line starts. + final double emulateVerticalOffsetBy; + /// Callback for the Header title final HeaderTitleCallback? onHeaderTitleTap; @@ -212,6 +227,8 @@ class WeekView extends StatefulWidget { this.initialDay, this.hourIndicatorSettings, this.hourLinePainter, + this.halfHourIndicatorSettings, + this.quarterHourIndicatorSettings, this.timeLineBuilder, this.timeLineWidth, this.liveTimeIndicatorSettings, @@ -239,6 +256,9 @@ class WeekView extends StatefulWidget { this.safeAreaOption = const SafeAreaOption(), this.fullDayEventBuilder, this.onHeaderTitleTap, + this.showHalfHours = false, + this.showQuarterHours = false, + this.emulateVerticalOffsetBy = 0, }) : assert(!(onHeaderTitleTap != null && weekPageHeaderBuilder != null), "can't use [onHeaderTitleTap] & [weekPageHeaderBuilder] simultaneously"), assert((timeLineOffset) >= 0, @@ -278,7 +298,9 @@ class WeekViewState extends State> { late HourIndicatorSettings _hourIndicatorSettings; late CustomHourLinePainter _hourLinePainter; + late HourIndicatorSettings _halfHourIndicatorSettings; late HourIndicatorSettings _liveTimeIndicatorSettings; + late HourIndicatorSettings _quarterHourIndicatorSettings; late PageController _pageController; @@ -441,6 +463,10 @@ class WeekViewState extends State> { heightPerMinute: widget.heightPerMinute, hourIndicatorSettings: _hourIndicatorSettings, hourLinePainter: _hourLinePainter, + halfHourIndicatorSettings: + _halfHourIndicatorSettings, + quarterHourIndicatorSettings: + _quarterHourIndicatorSettings, dates: dates, showLiveLine: widget.showLiveTimeLineInAllDays || _showLiveTimeIndicator(dates), @@ -456,6 +482,10 @@ class WeekViewState extends State> { minuteSlotSize: widget.minuteSlotSize, scrollConfiguration: _scrollConfiguration, fullDayEventBuilder: _fullDayEventBuilder, + showHalfHours: widget.showHalfHours, + showQuarterHours: widget.showQuarterHours, + emulateVerticalOffsetBy: + widget.emulateVerticalOffsetBy, ), ); }, @@ -533,6 +563,24 @@ class WeekViewState extends State> { _weekTitleWidth = (_width - _timeLineWidth - _hourIndicatorSettings.offset) / _totalDaysInWeek; + + _halfHourIndicatorSettings = widget.halfHourIndicatorSettings ?? + HourIndicatorSettings( + height: widget.heightPerMinute, + color: Constants.defaultBorderColor, + offset: 5, + ); + + assert(_halfHourIndicatorSettings.height < _hourHeight, + "halfHourIndicator height must be less than minuteHeight * 60"); + + _quarterHourIndicatorSettings = widget.quarterHourIndicatorSettings ?? + HourIndicatorSettings( + color: Constants.defaultBorderColor, + ); + + assert(_quarterHourIndicatorSettings.height < _hourHeight, + "quarterHourIndicator height must be less than minuteHeight * 60"); } void _calculateHeights() { @@ -689,8 +737,13 @@ class WeekViewState extends State> { /// [widget.eventTileBuilder] is null /// Widget _defaultTimeLineBuilder(DateTime date) { - final timeLineString = widget.timeLineStringBuilder?.call(date) ?? - "${((date.hour - 1) % 12) + 1} ${date.hour ~/ 12 == 0 ? "am" : "pm"}"; + final hour = ((date.hour - 1) % 12) + 1; + final timeLineString = (widget.timeLineStringBuilder != null) + ? widget.timeLineStringBuilder!(date) + : date.minute != 0 + ? "$hour:${date.minute}" + : "$hour ${date.hour ~/ 12 == 0 ? "am" : "pm"}"; + return Transform.translate( offset: Offset(0, -7.5), child: Padding( @@ -773,6 +826,7 @@ class WeekViewState extends State> { LineStyle lineStyle, double dashWidth, double dashSpaceWidth, + double emulateVerticalOffsetBy, ) { return HourLinePainter( lineColor: lineColor, @@ -784,6 +838,7 @@ class WeekViewState extends State> { lineStyle: lineStyle, dashWidth: dashWidth, dashSpaceWidth: dashSpaceWidth, + emulateVerticalOffsetBy: emulateVerticalOffsetBy, ); }