diff --git a/lib/src/ui/custom_widgets/current_time_mark_painter.dart b/lib/src/ui/custom_widgets/current_time_mark_painter.dart new file mode 100644 index 0000000..db9901b --- /dev/null +++ b/lib/src/ui/custom_widgets/current_time_mark_painter.dart @@ -0,0 +1,35 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_customizable_calendar/src/ui/themes/themes.dart'; + +class CurrentTimeMarkPainter extends CustomPainter { + const CurrentTimeMarkPainter({ + required this.currentTime, + required this.theme, + }) : super(repaint: currentTime); + + final ValueListenable currentTime; + + final TimeMarkTheme theme; + + @override + void paint(Canvas canvas, Size size) { + final secondExtent = size.height / Duration.secondsPerDay; + final dayDate = DateUtils.dateOnly(currentTime.value); + final timeDiff = currentTime.value.difference(dayDate); + final currentTimeOffset = timeDiff.inSeconds * secondExtent; + final dy = currentTimeOffset - theme.strokeWidth / 2; + final lineLength = theme.drawOnEventArea ? size.width : theme.length; + + canvas.drawLine( + Offset(0, dy), + Offset(lineLength, dy), + theme.painter, + ); + } + + @override + bool shouldRepaint(covariant CurrentTimeMarkPainter oldDelegate) => + theme != oldDelegate.theme || currentTime != oldDelegate.currentTime; +} \ No newline at end of file diff --git a/lib/src/ui/custom_widgets/event_area_hour_line_painter.dart b/lib/src/ui/custom_widgets/event_area_hour_line_painter.dart new file mode 100644 index 0000000..0af0c4f --- /dev/null +++ b/lib/src/ui/custom_widgets/event_area_hour_line_painter.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_customizable_calendar/src/ui/themes/themes.dart'; +import 'package:flutter_customizable_calendar/src/utils/utils.dart'; + +import 'current_time_mark_painter.dart'; + +/// It displays a time scale of a day view (with hours and minutes marks). +class EventAreaHourLinePainter extends StatefulWidget { + /// Creates view of a time scale. + const EventAreaHourLinePainter({ + super.key, + this.theme = const TimeScaleTheme(), + }); + + /// Customization params for the view + final TimeScaleTheme theme; + + @override + State createState() => _EventAreaHourLinePainterState(); +} + +class _EventAreaHourLinePainterState extends State { + final _clock = ClockNotifier.instance(); + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: Size( + MediaQuery.sizeOf(context).width, + widget.theme.hourExtent * Duration.hoursPerDay, + ), + painter: _hourLinePainter, + foregroundPainter: widget.theme.currentTimeMarkTheme.drawOnEventArea + ? _currentTimeMark + : null, + ); + } + + @override + void dispose() { + _clock.dispose(); + super.dispose(); + } + + CustomPainter get _hourLinePainter => _HourLinePainter( + dayDate: _clock.value, + theme: widget.theme, + ); + + CustomPainter get _currentTimeMark => CurrentTimeMarkPainter( + currentTime: _clock, + theme: widget.theme.currentTimeMarkTheme, + ); +} + +class _HourLinePainter extends CustomPainter { + final TimeScaleTheme theme; + final DateTime dayDate; + + _HourLinePainter({required this.theme, required this.dayDate}); + + @override + void paint(Canvas canvas, Size size) { + final hourExtent = size.height / Duration.hoursPerDay; + final quarterHeight = hourExtent / 4; + + for (var hour = 0; hour < Duration.hoursPerDay; hour++) { + final hourOffset = hourExtent * hour; + + // Draw half-hour marks + if (theme.drawHourMarks && theme.hourMarkTheme.drawOnEventArea) { + final line = theme.hourMarkTheme; + final dy = hourOffset - line.strokeWidth / 2; + + // Make sure the line has a start and end point that are different + canvas.drawLine( + Offset(0, dy), // Start point + Offset(size.width, dy), // End point + line.painter, + ); + } + + // Draw half-hour marks + if (theme.drawHalfHourMarks && theme.halfHourMarkTheme.drawOnEventArea) { + final line = theme.halfHourMarkTheme; + final dy = hourOffset + quarterHeight * 2 - line.strokeWidth / 2; + + canvas.drawLine( + Offset(0, dy), + Offset(size.width, dy), + line.painter, + ); + } + + if (theme.drawQuarterHourMarks && theme.quarterHourMarkTheme.drawOnEventArea) { + final line = theme.quarterHourMarkTheme; + final dy1 = hourOffset + quarterHeight - line.strokeWidth / 2; + final dy2 = hourOffset + quarterHeight * 3 - line.strokeWidth / 2; + + canvas + ..drawLine( + Offset(0, dy1), + Offset(0 + size.width, dy1), + line.painter, + ) + ..drawLine( + Offset(0, dy2), + Offset(0 + size.width, dy2), + line.painter, + ); + } + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return false; + } +} diff --git a/lib/src/ui/custom_widgets/time_scale.dart b/lib/src/ui/custom_widgets/time_scale.dart index 56527a9..b12ad80 100644 --- a/lib/src/ui/custom_widgets/time_scale.dart +++ b/lib/src/ui/custom_widgets/time_scale.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_customizable_calendar/src/ui/themes/themes.dart'; import 'package:flutter_customizable_calendar/src/utils/utils.dart'; +import 'package:flutter_customizable_calendar/src/ui/custom_widgets/current_time_mark_painter.dart'; /// It displays a time scale of a day view (with hours and minutes marks). class TimeScale extends StatefulWidget { @@ -48,7 +49,7 @@ class _TimeScaleState extends State { theme: widget.theme, ); - CustomPainter get _currentTimeMark => _CurrentTimeMarkPainter( + CustomPainter get _currentTimeMark => CurrentTimeMarkPainter( currentTime: _clock, theme: widget.theme.currentTimeMarkTheme, ); @@ -151,33 +152,3 @@ class _ScalePainter extends CustomPainter { } } } - -class _CurrentTimeMarkPainter extends CustomPainter { - const _CurrentTimeMarkPainter({ - required this.currentTime, - required this.theme, - }) : super(repaint: currentTime); - - final ValueListenable currentTime; - - final TimeMarkTheme theme; - - @override - void paint(Canvas canvas, Size size) { - final secondExtent = size.height / Duration.secondsPerDay; - final dayDate = DateUtils.dateOnly(currentTime.value); - final timeDiff = currentTime.value.difference(dayDate); - final currentTimeOffset = timeDiff.inSeconds * secondExtent; - final dy = currentTimeOffset - theme.strokeWidth / 2; - - canvas.drawLine( - Offset(0, dy), - Offset(theme.length, dy), - theme.painter, - ); - } - - @override - bool shouldRepaint(covariant _CurrentTimeMarkPainter oldDelegate) => - theme != oldDelegate.theme || currentTime != oldDelegate.currentTime; -} diff --git a/lib/src/ui/themes/time_mark_theme.dart b/lib/src/ui/themes/time_mark_theme.dart index 56f2fcb..34ad4fc 100644 --- a/lib/src/ui/themes/time_mark_theme.dart +++ b/lib/src/ui/themes/time_mark_theme.dart @@ -8,9 +8,10 @@ class TimeMarkTheme extends Equatable { this.color = Colors.grey, this.strokeWidth = 2, this.strokeCap = StrokeCap.square, + this.drawOnEventArea = false }); - /// Length of the line + /// Length of the line on the time scale final double length; /// Color of the line @@ -22,6 +23,9 @@ class TimeMarkTheme extends Equatable { /// The kind of finish to place on the end of line final StrokeCap strokeCap; + /// Enable the mark on the event area + final bool drawOnEventArea; + /// A painter which contains the given parameters Paint get painter => Paint() ..color = color @@ -43,12 +47,14 @@ class TimeMarkTheme extends Equatable { Color? color, double? strokeWidth, StrokeCap? strokeCap, + bool? drawOnEventArea, }) { return TimeMarkTheme( length: length ?? this.length, color: color ?? this.color, strokeWidth: strokeWidth ?? this.strokeWidth, strokeCap: strokeCap ?? this.strokeCap, + drawOnEventArea: drawOnEventArea ?? this.drawOnEventArea, ); } } diff --git a/lib/src/ui/themes/time_scale_theme.dart b/lib/src/ui/themes/time_scale_theme.dart index 93ef600..726bb30 100644 --- a/lib/src/ui/themes/time_scale_theme.dart +++ b/lib/src/ui/themes/time_scale_theme.dart @@ -24,6 +24,11 @@ class TimeScaleTheme extends Equatable { length: 48, color: Colors.red, ), + this.drawHourMarks = false, + this.hourMarkTheme = const TimeMarkTheme( + length: 0, + drawOnEventArea: true, + ), this.drawHalfHourMarks = true, this.halfHourMarkTheme = const TimeMarkTheme(length: 16), this.drawQuarterHourMarks = true, @@ -45,6 +50,12 @@ class TimeScaleTheme extends Equatable { /// Current time mark customization parameters final TimeMarkTheme currentTimeMarkTheme; + /// Whether a hour mark is need to be shown or not + final bool drawHourMarks; + + /// An hour mark customization theme + final TimeMarkTheme hourMarkTheme; + /// Whether a half of an hour mark is need to show final bool drawHalfHourMarks; @@ -86,6 +97,8 @@ class TimeScaleTheme extends Equatable { double? width, double? hourExtent, TimeMarkTheme? currentTimeMarkTheme, + bool? drawHourMarks, + TimeMarkTheme? hourMarkTheme, bool? drawHalfHourMarks, TimeMarkTheme? halfHourMarkTheme, bool? drawQuarterHourMarks, @@ -98,6 +111,8 @@ class TimeScaleTheme extends Equatable { width: width ?? this.width, hourExtent: hourExtent ?? this.hourExtent, currentTimeMarkTheme: currentTimeMarkTheme ?? this.currentTimeMarkTheme, + drawHourMarks: drawHourMarks ?? this.drawHourMarks, + hourMarkTheme: hourMarkTheme ?? this.hourMarkTheme, drawHalfHourMarks: drawHalfHourMarks ?? this.drawHalfHourMarks, halfHourMarkTheme: halfHourMarkTheme ?? this.halfHourMarkTheme, drawQuarterHourMarks: drawQuarterHourMarks ?? this.drawQuarterHourMarks, diff --git a/lib/src/ui/views/days_view.dart b/lib/src/ui/views/days_view.dart index 4577191..daf95c1 100644 --- a/lib/src/ui/views/days_view.dart +++ b/lib/src/ui/views/days_view.dart @@ -9,6 +9,7 @@ import 'package:flutter_customizable_calendar/src/domain/models/models.dart'; import 'package:flutter_customizable_calendar/src/ui/controllers/controllers.dart'; import 'package:flutter_customizable_calendar/src/ui/custom_widgets/all_days_events_list.dart'; import 'package:flutter_customizable_calendar/src/ui/custom_widgets/custom_widgets.dart'; +import 'package:flutter_customizable_calendar/src/ui/custom_widgets/event_area_hour_line_painter.dart'; import 'package:flutter_customizable_calendar/src/ui/themes/themes.dart'; import 'package:flutter_customizable_calendar/src/utils/utils.dart'; @@ -627,22 +628,29 @@ class _DaysViewState extends State> theme: theme.timeScaleTheme, ), Expanded( - child: ColoredBox( - color: Colors.transparent, // Needs for hitTesting - child: EventsLayout( - dayDate: dayDate, - viewType: CalendarView.days, - overlayKey: _overlayKey, - layoutsKeys: DaysViewKeys.layouts, - eventsKeys: DaysViewKeys.events, - timelineTheme: widget.timelineTheme, - breaks: widget.breaks, - events: _events, - elevatedEvent: _elevatedEvent, - onEventTap: widget.onEventTap, - eventBuilders: widget.eventBuilders, - ), - ), + child: Stack( + children: [ + EventAreaHourLinePainter( + theme: theme.timeScaleTheme, + ), + ColoredBox( + color: Colors.transparent, // Needs for hitTesting + child: EventsLayout( + dayDate: dayDate, + viewType: CalendarView.days, + overlayKey: _overlayKey, + layoutsKeys: DaysViewKeys.layouts, + eventsKeys: DaysViewKeys.events, + timelineTheme: widget.timelineTheme, + breaks: widget.breaks, + events: _events, + elevatedEvent: _elevatedEvent, + onEventTap: widget.onEventTap, + eventBuilders: widget.eventBuilders, + ), + ), + ], + ) ), ], ), diff --git a/lib/src/ui/views/week_view/week_view_timeline_page.dart b/lib/src/ui/views/week_view/week_view_timeline_page.dart index ec4a196..a4cd57e 100644 --- a/lib/src/ui/views/week_view/week_view_timeline_page.dart +++ b/lib/src/ui/views/week_view/week_view_timeline_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_customizable_calendar/flutter_customizable_calendar.dart'; import 'package:flutter_customizable_calendar/src/ui/custom_widgets/all_days_events_list.dart'; +import 'package:flutter_customizable_calendar/src/ui/custom_widgets/event_area_hour_line_painter.dart'; import 'package:flutter_customizable_calendar/src/ui/views/week_view/week_view_timeline_widget.dart'; import 'package:flutter_customizable_calendar/src/utils/utils.dart'; @@ -268,7 +269,6 @@ class _WeekViewTimelinePageState WeekViewTimelineWidget( days: weekDays, scrollTo: (offset) { - if (offset == .0) return; _timelineController.jumpTo(offset); }, initialScrollOffset: @@ -401,20 +401,26 @@ class _WeekViewTimelinePageState bottom: widget.theme.padding.bottom, ), color: Colors.transparent, // Needs for hitTesting - child: EventsLayout( - // key: ValueKey(dayDate), - dayDate: dayDate, - eventBuilders: widget.eventBuilders, - viewType: CalendarView.week, - overlayKey: widget.overlayKey, - layoutsKeys: widget.layoutKeys, - eventsKeys: widget.eventKeys, - timelineTheme: widget.theme, - breaks: widget.breaks, - events: widget.events, - elevatedEvent: widget.elevatedEvent, - onEventTap: widget.onEventTap, - ), + child: Stack( + children: [ + EventAreaHourLinePainter( + theme: widget.theme.timeScaleTheme, + ), + EventsLayout( + key: ValueKey(dayDate), + dayDate: dayDate, + eventBuilders: widget.eventBuilders, + viewType: CalendarView.week, + overlayKey: widget.overlayKey, + layoutsKeys: widget.layoutKeys, + eventsKeys: widget.eventKeys, + timelineTheme: widget.theme, + breaks: widget.breaks, + events: widget.events, + elevatedEvent: widget.elevatedEvent, + onEventTap: widget.onEventTap, + ), + ]) ), ), );