diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index c899dad119..7e70ebf8ff 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -633,7 +633,7 @@ class _SearchResultGrid extends ConsumerWidget { groupBy: GroupAssetsBy.none, appBar: null, bottomSheet: const GeneralBottomSheet(minChildSize: 0.20), - withScrubber: false, + snapToMonth: false, ), ), ), diff --git a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart index 19f393d9fc..58d7f933e9 100644 --- a/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/scrubber.widget.dart @@ -31,6 +31,11 @@ class Scrubber extends ConsumerStatefulWidget { final double? monthSegmentSnappingOffset; + final bool snapToMonth; + + /// Whether an app bar is present, affects coordinate calculations + final bool hasAppBar; + Scrubber({ super.key, Key? scrollThumbKey, @@ -39,6 +44,8 @@ class Scrubber extends ConsumerStatefulWidget { this.topPadding = 0, this.bottomPadding = 0, this.monthSegmentSnappingOffset, + this.snapToMonth = true, + this.hasAppBar = true, required this.child, }) : assert(child.scrollDirection == Axis.vertical); @@ -232,7 +239,7 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi } } - if (_monthCount < kMinMonthsToEnableScrubberSnap) { + if (_monthCount < kMinMonthsToEnableScrubberSnap || !widget.snapToMonth) { // If there are less than kMinMonthsToEnableScrubberSnap months, we don't need to snap to segments setState(() { _thumbTopOffset = dragPosition; @@ -259,14 +266,28 @@ class ScrubberState extends ConsumerState with TickerProviderStateMixi /// - If user drags to global Y position that's 100 pixels from the top /// - The relative position would be 100 - 50 = 50 (50 pixels into the scrubber area) double _calculateDragPosition(DragUpdateDetails details) { + if (widget.hasAppBar) { + final dragAreaTop = widget.topPadding; + final dragAreaBottom = widget.timelineHeight - widget.bottomPadding; + final dragAreaHeight = dragAreaBottom - dragAreaTop; + + final relativePosition = details.globalPosition.dy - dragAreaTop; + + // Make sure the position stays within the scrubber's bounds + return relativePosition.clamp(0.0, dragAreaHeight); + } + + // Get the local position relative to the gesture detector + final RenderBox? renderBox = context.findRenderObject() as RenderBox?; + if (renderBox != null) { + final localPosition = renderBox.globalToLocal(details.globalPosition); + return localPosition.dy.clamp(0.0, _scrubberHeight); + } + + // Fallback to current logic if render box is not available final dragAreaTop = widget.topPadding; - final dragAreaBottom = widget.timelineHeight - widget.bottomPadding; - final dragAreaHeight = dragAreaBottom - dragAreaTop; - final relativePosition = details.globalPosition.dy - dragAreaTop; - - // Make sure the position stays within the scrubber's bounds - return relativePosition.clamp(0.0, dragAreaHeight); + return relativePosition.clamp(0.0, _scrubberHeight); } /// Find the segment closest to the given position diff --git a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart index 19c3551e4a..7ac9a4aaee 100644 --- a/mobile/lib/presentation/widgets/timeline/timeline.widget.dart +++ b/mobile/lib/presentation/widgets/timeline/timeline.widget.dart @@ -38,6 +38,7 @@ class Timeline extends StatelessWidget { this.bottomSheet = const GeneralBottomSheet(minChildSize: 0.18), this.groupBy, this.withScrubber = true, + this.snapToMonth = true, }); final Widget? topSliverWidget; @@ -48,6 +49,7 @@ class Timeline extends StatelessWidget { final bool withStack; final GroupAssetsBy? groupBy; final bool withScrubber; + final bool snapToMonth; @override Widget build(BuildContext context) { @@ -73,6 +75,7 @@ class Timeline extends StatelessWidget { appBar: appBar, bottomSheet: bottomSheet, withScrubber: withScrubber, + snapToMonth: snapToMonth, ), ), ), @@ -87,6 +90,7 @@ class _SliverTimeline extends ConsumerStatefulWidget { this.appBar, this.bottomSheet, this.withScrubber = true, + this.snapToMonth = true, }); final Widget? topSliverWidget; @@ -94,6 +98,7 @@ class _SliverTimeline extends ConsumerStatefulWidget { final Widget? appBar; final Widget? bottomSheet; final bool withScrubber; + final bool snapToMonth; @override ConsumerState createState() => _SliverTimelineState(); @@ -309,11 +314,13 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> { final Widget timeline; if (widget.withScrubber) { timeline = Scrubber( + snapToMonth: widget.snapToMonth, layoutSegments: segments, timelineHeight: maxHeight, topPadding: topPadding, bottomPadding: bottomPadding, monthSegmentSnappingOffset: widget.topSliverWidgetHeight ?? 0 + appBarExpandedHeight, + hasAppBar: widget.appBar != null, child: grid, ); } else {