diff --git a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/cupertino_scroll_bar_with_use_notifier.dart b/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/cupertino_scroll_bar_with_use_notifier.dart deleted file mode 100644 index fc97d03f52..0000000000 --- a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/cupertino_scroll_bar_with_use_notifier.dart +++ /dev/null @@ -1,236 +0,0 @@ -// Modified CupertinoScrollbar that accepts a ValueNotifier to indicate -// if the scrollbar is in use. In flutter CupertinoScrollbar is in a different -// file where as MaterialScrollbar is in the same file as ScrollBar. So -// following the same convention by create a separate file for -// CupertinoScrollbarWithUseNotifier. - -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import "package:flutter/cupertino.dart"; -import 'package:flutter/services.dart'; - -// All values eyeballed. -const double _kScrollbarMinOverscrollLength = 8.0; -const Duration _kScrollbarTimeToFade = Duration(milliseconds: 1200); -const Duration _kScrollbarFadeDuration = Duration(milliseconds: 250); -const Duration _kScrollbarResizeDuration = Duration(milliseconds: 100); - -// Extracted from iOS 13.1 beta using Debug View Hierarchy. -const Color _kScrollbarColor = CupertinoDynamicColor.withBrightness( - color: Color(0x59000000), - darkColor: Color(0x80FFFFFF), -); - -// This is the amount of space from the top of a vertical scrollbar to the -// top edge of the scrollable, measured when the vertical scrollbar overscrolls -// to the top. -// TODO(LongCatIsLooong): fix https://github.com/flutter/flutter/issues/32175 -const double _kScrollbarMainAxisMargin = 3.0; -const double _kScrollbarCrossAxisMargin = 3.0; - -/// An iOS style scrollbar. -/// -/// To add a scrollbar to a [ScrollView], wrap the scroll view widget in -/// a [CupertinoScrollbarWithUseNotifier] widget. -/// -/// {@youtube 560 315 https://www.youtube.com/watch?v=DbkIQSvwnZc} -/// -/// {@macro flutter.widgets.Scrollbar} -/// -/// When dragging a [CupertinoScrollbarWithUseNotifier] thumb, the thickness and radius will -/// animate from [thickness] and [radius] to [thicknessWhileDragging] and -/// [radiusWhileDragging], respectively. -/// -/// {@tool dartpad} -/// This sample shows a [CupertinoScrollbarWithUseNotifier] that fades in and out of view as scrolling occurs. -/// The scrollbar will fade into view as the user scrolls, and fade out when scrolling stops. -/// The `thickness` of the scrollbar will animate from 6 pixels to the `thicknessWhileDragging` of 10 -/// when it is dragged by the user. The `radius` of the scrollbar thumb corners will animate from 34 -/// to the `radiusWhileDragging` of 0 when the scrollbar is being dragged by the user. -/// -/// ** See code in examples/api/lib/cupertino/scrollbar/cupertino_scrollbar.0.dart ** -/// {@end-tool} -/// -/// {@tool dartpad} -/// When [thumbVisibility] is true, the scrollbar thumb will remain visible without the -/// fade animation. This requires that a [ScrollController] is provided to controller, -/// or that the [PrimaryScrollController] is available. -/// -/// ** See code in examples/api/lib/cupertino/scrollbar/cupertino_scrollbar.1.dart ** -/// {@end-tool} -/// -/// See also: -/// -/// * [ListView], which displays a linear, scrollable list of children. -/// * [GridView], which displays a 2 dimensional, scrollable array of children. -/// * [Scrollbar], a Material Design scrollbar. -/// * [RawScrollbar], a basic scrollbar that fades in and out, extended -/// by this class to add more animations and behaviors. -class CupertinoScrollbarWithUseNotifier extends RawScrollbar { - /// Creates an iOS style scrollbar that wraps the given [child]. - /// - /// The [child] should be a source of [ScrollNotification] notifications, - /// typically a [Scrollable] widget. - - final ValueNotifier inUseNotifier; - final double minScrollbarLength; - - const CupertinoScrollbarWithUseNotifier({ - super.key, - required super.child, - required this.inUseNotifier, - required this.minScrollbarLength, - super.controller, - bool? thumbVisibility, - double super.thickness = defaultThickness, - this.thicknessWhileDragging = defaultThicknessWhileDragging, - Radius super.radius = defaultRadius, - this.radiusWhileDragging = defaultRadiusWhileDragging, - ScrollNotificationPredicate? notificationPredicate, - super.scrollbarOrientation, - }) : assert(thickness < double.infinity), - assert(thicknessWhileDragging < double.infinity), - super( - thumbVisibility: thumbVisibility ?? false, - fadeDuration: _kScrollbarFadeDuration, - timeToFade: _kScrollbarTimeToFade, - pressDuration: const Duration(milliseconds: 100), - notificationPredicate: - notificationPredicate ?? defaultScrollNotificationPredicate, - ); - - /// Default value for [thickness] if it's not specified in [CupertinoScrollbarWithUseNotifier]. - static const double defaultThickness = 3; - - /// Default value for [thicknessWhileDragging] if it's not specified in - /// [CupertinoScrollbarWithUseNotifier]. - static const double defaultThicknessWhileDragging = 8.0; - - /// Default value for [radius] if it's not specified in [CupertinoScrollbarWithUseNotifier]. - static const Radius defaultRadius = Radius.circular(1.5); - - /// Default value for [radiusWhileDragging] if it's not specified in - /// [CupertinoScrollbarWithUseNotifier]. - static const Radius defaultRadiusWhileDragging = Radius.circular(4.0); - - /// The thickness of the scrollbar when it's being dragged by the user. - /// - /// When the user starts dragging the scrollbar, the thickness will animate - /// from [thickness] to this value, then animate back when the user stops - /// dragging the scrollbar. - final double thicknessWhileDragging; - - /// The radius of the scrollbar edges when the scrollbar is being dragged by - /// the user. - /// - /// When the user starts dragging the scrollbar, the radius will animate - /// from [radius] to this value, then animate back when the user stops - /// dragging the scrollbar. - final Radius radiusWhileDragging; - - @override - RawScrollbarState createState() => - _CupertinoScrollbarState(); -} - -class _CupertinoScrollbarState - extends RawScrollbarState { - late AnimationController _thicknessAnimationController; - - double get _thickness { - return widget.thickness! + - _thicknessAnimationController.value * - (widget.thicknessWhileDragging - widget.thickness!); - } - - Radius get _radius { - return Radius.lerp( - widget.radius, - widget.radiusWhileDragging, - _thicknessAnimationController.value, - )!; - } - - @override - void initState() { - super.initState(); - _thicknessAnimationController = AnimationController( - vsync: this, - duration: _kScrollbarResizeDuration, - ); - _thicknessAnimationController.addListener(() { - updateScrollbarPainter(); - }); - } - - @override - void updateScrollbarPainter() { - scrollbarPainter - ..color = CupertinoDynamicColor.resolve(_kScrollbarColor, context) - ..textDirection = Directionality.of(context) - ..thickness = _thickness - ..mainAxisMargin = _kScrollbarMainAxisMargin - ..crossAxisMargin = _kScrollbarCrossAxisMargin - ..radius = _radius - ..padding = MediaQuery.paddingOf(context) - ..minLength = widget.minScrollbarLength - ..minOverscrollLength = _kScrollbarMinOverscrollLength - ..scrollbarOrientation = widget.scrollbarOrientation; - } - - double _pressStartAxisPosition = 0.0; - - // Long press event callbacks handle the gesture where the user long presses - // on the scrollbar thumb and then drags the scrollbar without releasing. - - @override - void handleThumbPressStart(Offset localPosition) { - super.handleThumbPressStart(localPosition); - widget.inUseNotifier.value = true; - final Axis? direction = getScrollbarDirection(); - if (direction == null) { - return; - } - _pressStartAxisPosition = switch (direction) { - Axis.vertical => localPosition.dy, - Axis.horizontal => localPosition.dx, - }; - } - - @override - void handleThumbPress() { - if (getScrollbarDirection() == null) { - return; - } - super.handleThumbPress(); - _thicknessAnimationController.forward().then( - (_) => HapticFeedback.mediumImpact(), - ); - } - - @override - void handleThumbPressEnd(Offset localPosition, Velocity velocity) { - widget.inUseNotifier.value = false; - final Axis? direction = getScrollbarDirection(); - if (direction == null) { - return; - } - _thicknessAnimationController.reverse(); - super.handleThumbPressEnd(localPosition, velocity); - final (double axisPosition, double axisVelocity) = switch (direction) { - Axis.horizontal => (localPosition.dx, velocity.pixelsPerSecond.dx), - Axis.vertical => (localPosition.dy, velocity.pixelsPerSecond.dy), - }; - if (axisPosition != _pressStartAxisPosition && axisVelocity.abs() < 10) { - HapticFeedback.mediumImpact(); - } - } - - @override - void dispose() { - _thicknessAnimationController.dispose(); - super.dispose(); - } -} diff --git a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/custom_scroll_bar.dart b/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/custom_scroll_bar.dart index 99ca79b11f..e220ca90ea 100644 --- a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/custom_scroll_bar.dart +++ b/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/custom_scroll_bar.dart @@ -52,6 +52,7 @@ class _CustomScrollBarState extends State { double? heightOfScrollbarDivider; double? heightOfScrollTrack; late bool _showScrollbarDivisions; + late bool _showThumb; // Scrollbar's heigh is not fixed by default. If the scrollable is short // enough, the scrollbar's height can go above the minimum length. @@ -89,6 +90,13 @@ class _CustomScrollBarState extends State { _showScrollbarDivisions = false; } + if (widget.galleryGroups.groupLayouts.last.maxOffset > + widget.heighOfViewport * 3) { + _showThumb = true; + } else { + _showThumb = false; + } + if (_showScrollbarDivisions) { getIntrinsicSizeOfWidget(const ScrollBarDivider(title: "Temp"), context) .then((size) { @@ -215,6 +223,7 @@ class _CustomScrollBarState extends State { padding: EdgeInsets.only( bottom: widget.bottomPadding.value, top: widget.topPadding, + right: 4, ), ), child: ScrollbarWithUseNotifer( @@ -223,6 +232,9 @@ class _CustomScrollBarState extends State { interactive: true, inUseNotifier: widget.inUseNotifier, minScrollbarLength: _kScrollbarMinLength, + showThumb: _showThumb, + radius: const Radius.circular(4), + thickness: 8, child: widget.child, ), ), diff --git a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/scroll_bar_with_use_notifier.dart b/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/scroll_bar_with_use_notifier.dart index 75551b6d54..56e656974f 100644 --- a/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/scroll_bar_with_use_notifier.dart +++ b/mobile/apps/photos/lib/ui/viewer/gallery/scrollbar/scroll_bar_with_use_notifier.dart @@ -8,7 +8,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/gestures.dart'; import "package:flutter/material.dart"; -import "package:photos/ui/viewer/gallery/scrollbar/cupertino_scroll_bar_with_use_notifier.dart"; const double _kScrollbarThickness = 8.0; const double _kScrollbarThicknessWithTrack = 12.0; @@ -96,6 +95,7 @@ class ScrollbarWithUseNotifer extends StatelessWidget { this.notificationPredicate, this.interactive, this.scrollbarOrientation, + this.showThumb, }); /// {@macro flutter.widgets.Scrollbar.child} @@ -155,25 +155,10 @@ class ScrollbarWithUseNotifer extends StatelessWidget { final double minScrollbarLength; + final bool? showThumb; + @override Widget build(BuildContext context) { - if (Theme.of(context).platform == TargetPlatform.iOS) { - return CupertinoScrollbarWithUseNotifier( - thumbVisibility: thumbVisibility ?? false, - thickness: thickness ?? CupertinoScrollbar.defaultThickness, - thicknessWhileDragging: - thickness ?? CupertinoScrollbar.defaultThicknessWhileDragging, - radius: radius ?? CupertinoScrollbar.defaultRadius, - radiusWhileDragging: - radius ?? CupertinoScrollbar.defaultRadiusWhileDragging, - controller: controller, - notificationPredicate: notificationPredicate, - scrollbarOrientation: scrollbarOrientation, - inUseNotifier: inUseNotifier, - minScrollbarLength: minScrollbarLength, - child: child, - ); - } return _MaterialScrollbar( controller: controller, thumbVisibility: thumbVisibility, @@ -185,6 +170,7 @@ class ScrollbarWithUseNotifer extends StatelessWidget { scrollbarOrientation: scrollbarOrientation, inUseNotifier: inUseNotifier, minScrollbarLength: minScrollbarLength, + showThumb: showThumb, child: child, ); } @@ -193,10 +179,12 @@ class ScrollbarWithUseNotifer extends StatelessWidget { class _MaterialScrollbar extends RawScrollbar { final ValueNotifier inUseNotifier; final double minScrollbarLength; + final bool? showThumb; const _MaterialScrollbar({ required super.child, required this.inUseNotifier, required this.minScrollbarLength, + required this.showThumb, super.controller, super.thumbVisibility, super.trackVisibility, @@ -251,6 +239,9 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> { }; MaterialStateProperty get _thumbColor { + if (widget.showThumb == false) { + return MaterialStateProperty.all(const Color(0x00000000)); + } final Color onSurface = _colorScheme.onSurface; final Brightness brightness = _colorScheme.brightness; late Color dragColor; @@ -291,6 +282,9 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> { } MaterialStateProperty get _trackColor { + if (widget.showThumb == false) { + return MaterialStateProperty.all(const Color(0x00000000)); + } final Color onSurface = _colorScheme.onSurface; final Brightness brightness = _colorScheme.brightness; return MaterialStateProperty.resolveWith((Set states) { @@ -306,6 +300,9 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> { } MaterialStateProperty get _trackBorderColor { + if (widget.showThumb == false) { + return MaterialStateProperty.all(const Color(0x00000000)); + } final Color onSurface = _colorScheme.onSurface; final Brightness brightness = _colorScheme.brightness; return MaterialStateProperty.resolveWith((Set states) {