From 4fcd1136f78c1c80adb268a232c7b790a2dba803 Mon Sep 17 00:00:00 2001 From: solid-maksymtielnyi Date: Wed, 1 Jul 2026 00:54:28 +0300 Subject: [PATCH 1/2] Add built-in horizontal scroll bar to CategoryOverflowCalendarDayView --- .../category_overflow_calendar_day_view.dart | 87 ++++++++++++------- lib/src/models/scrollbar_visibility.dart | 6 ++ 2 files changed, 64 insertions(+), 29 deletions(-) create mode 100644 lib/src/models/scrollbar_visibility.dart diff --git a/lib/src/day_views/category/category_overflow_calendar_day_view.dart b/lib/src/day_views/category/category_overflow_calendar_day_view.dart index 9c3b603..fdd4749 100644 --- a/lib/src/day_views/category/category_overflow_calendar_day_view.dart +++ b/lib/src/day_views/category/category_overflow_calendar_day_view.dart @@ -2,6 +2,7 @@ import 'dart:math'; import 'package:calendar_day_view/src/extensions/list_extensions.dart'; import 'package:calendar_day_view/src/extensions/time_of_day_extension.dart'; +import 'package:calendar_day_view/src/models/scrollbar_visibility.dart'; import 'package:calendar_day_view/src/widgets/timed_rebuilder.dart'; import 'package:flutter/material.dart'; import 'package:timezone/timezone.dart'; @@ -132,6 +133,7 @@ class CategoryOverflowCalendarDayView extends StatefulWidget this.horizontalScrollControllerGroup, this.verticalScrollControllerGroup, this.onLayoutMetrics, + this.horizontalScrollbarVisibility = ScrollbarVisibility.never, ValueGetter? clock, }) : clock = clock ?? DateTime.now, super(key: key); @@ -142,6 +144,7 @@ class CategoryOverflowCalendarDayView extends StatefulWidget final LinkedScrollControllerGroup? horizontalScrollControllerGroup; final LinkedScrollControllerGroup? verticalScrollControllerGroup; final CategoryDayViewLayoutMetricsCallback? onLayoutMetrics; + final ScrollbarVisibility horizontalScrollbarVisibility; final ValueGetter clock; final GroupingStrategy? groupingStrategy; final GroupLayoutStrategy? groupLayoutStrategy; @@ -269,6 +272,8 @@ class _CategoryOverflowCalendarDayViewState final totalWidth = max(minTotalWidth, constraints.maxWidth); final rowLength = totalWidth - widget.timeColumnWidth; final tileWidth = rowLength / categoriesCount; + final isHorizontallyOverflowing = + minTotalWidth > constraints.maxWidth; final onLayoutMetrics = widget.onLayoutMetrics; @@ -352,38 +357,42 @@ class _CategoryOverflowCalendarDayViewState decoration: BoxDecoration( border: widget.tableBodyBorder, color: Colors.black), - child: SingleChildScrollView( - controller: _vertScrollController, - clipBehavior: Clip.none, - physics: const ClampingScrollPhysics(), + child: _wrapWithHorizontalScrollbar( + isOverflowing: isHorizontallyOverflowing, child: SingleChildScrollView( - controller: _horizScrollController, + controller: _vertScrollController, clipBehavior: Clip.none, physics: const ClampingScrollPhysics(), - scrollDirection: Axis.horizontal, - child: SizedBox( - width: rowLength, - child: _DayViewBody( - timeList: timeList, - rowHeight: rowHeight, - tileWidth: tileWidth, - evenRowColor: widget.evenRowColor, - oddRowColor: widget.oddRowColor, - rowBuilder: widget.backgroundTimeTileBuilder, - events: widget.events, - categories: widget.categories, - verticalDivider: widget.verticalDivider, - timeGap: widget.timeGap, - heightPerMin: widget.heightPerMin, - timeTextStyle: widget.timeTextStyle, - eventBuilder: widget.eventBuilder, - horizontalDivider: widget.horizontalDivider, - onTileTap: widget.onTileTap, - groupingStrategy: widget.groupingStrategy ?? - NoGroupingStrategy(), - groupLayoutStrategy: - widget.groupLayoutStrategy, - clock: widget.clock, + child: SingleChildScrollView( + controller: _horizScrollController, + clipBehavior: Clip.none, + physics: const ClampingScrollPhysics(), + scrollDirection: Axis.horizontal, + child: SizedBox( + width: rowLength, + child: _DayViewBody( + timeList: timeList, + rowHeight: rowHeight, + tileWidth: tileWidth, + evenRowColor: widget.evenRowColor, + oddRowColor: widget.oddRowColor, + rowBuilder: + widget.backgroundTimeTileBuilder, + events: widget.events, + categories: widget.categories, + verticalDivider: widget.verticalDivider, + timeGap: widget.timeGap, + heightPerMin: widget.heightPerMin, + timeTextStyle: widget.timeTextStyle, + eventBuilder: widget.eventBuilder, + horizontalDivider: widget.horizontalDivider, + onTileTap: widget.onTileTap, + groupingStrategy: widget.groupingStrategy ?? + NoGroupingStrategy(), + groupLayoutStrategy: + widget.groupLayoutStrategy, + clock: widget.clock, + ), ), ), ), @@ -401,6 +410,26 @@ class _CategoryOverflowCalendarDayViewState ); } + Widget _wrapWithHorizontalScrollbar({ + required bool isOverflowing, + required Widget child, + }) { + final visibility = widget.horizontalScrollbarVisibility; + + if (visibility == ScrollbarVisibility.never || !isOverflowing) { + return child; + } + + return Scrollbar( + controller: _horizScrollController, + thumbVisibility: visibility == ScrollbarVisibility.always, + scrollbarOrientation: ScrollbarOrientation.bottom, + notificationPredicate: (notification) => + notification.metrics.axis == Axis.horizontal, + child: child, + ); + } + @override void dispose() { _headerScrollController.dispose(); diff --git a/lib/src/models/scrollbar_visibility.dart b/lib/src/models/scrollbar_visibility.dart new file mode 100644 index 0000000..9ba3bf1 --- /dev/null +++ b/lib/src/models/scrollbar_visibility.dart @@ -0,0 +1,6 @@ +/// Controls when the horizontal scrollbar is shown. +enum ScrollbarVisibility { + never, + always, + whileScrolling, +} From 7a16749b57902272af8ec76608ca4c0913459f21 Mon Sep 17 00:00:00 2001 From: solid-maksymtielnyi Date: Wed, 1 Jul 2026 01:06:52 +0300 Subject: [PATCH 2/2] Export ScrollbarVisibility --- lib/calendar_day_view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/calendar_day_view.dart b/lib/calendar_day_view.dart index 3feb5ea..339916c 100644 --- a/lib/calendar_day_view.dart +++ b/lib/calendar_day_view.dart @@ -17,4 +17,5 @@ export 'src/day_views/overflow/overflow_calendar_day_view.dart'; export 'src/models/categorized_day_event.dart'; export 'src/models/day_event.dart'; export 'src/models/event_category.dart'; +export 'src/models/scrollbar_visibility.dart'; export 'src/models/typedef.dart';