From 610ca324c3f6edba7104f2013f0c8464af1d29af Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Fri, 24 Sep 2021 16:13:05 -0500 Subject: [PATCH] Update scrollbar for hover events (#90636) --- .../flutter/lib/src/widgets/scrollbar.dart | 49 +++++++++++-------- .../flutter/test/widgets/scrollbar_test.dart | 35 +++++++++++++ 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart index 0944c07fdf2..81e1a7a6d9d 100644 --- a/packages/flutter/lib/src/widgets/scrollbar.dart +++ b/packages/flutter/lib/src/widgets/scrollbar.dart @@ -462,29 +462,37 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { break; } + // Whether we paint or not, calculating these rects allows us to hit test + // when the scrollbar is transparent. _trackRect = trackOffset & trackSize; - canvas.drawRect(_trackRect!, _paintTrack()); - canvas.drawLine( - trackOffset, - Offset(trackOffset.dx, trackOffset.dy + _trackExtent), - _paintTrack(isBorder: true), - ); - _thumbRect = Offset(x, y) & thumbSize; - if (radius != null) { - canvas.drawRRect(RRect.fromRectAndRadius(_thumbRect!, radius!), _paintThumb); - return; + // Paint if the opacity dictates visibility + if (fadeoutOpacityAnimation.value != 0.0) { + // Track + canvas.drawRect(_trackRect!, _paintTrack()); + // Track Border + canvas.drawLine( + trackOffset, + Offset(trackOffset.dx, trackOffset.dy + _trackExtent), + _paintTrack(isBorder: true), + ); + if (radius != null) { + // Rounded rect thumb + canvas.drawRRect( + RRect.fromRectAndRadius(_thumbRect!, radius!), _paintThumb); + return; + } + if (shape == null) { + // Square thumb + canvas.drawRect(_thumbRect!, _paintThumb); + return; + } + // Custom-shaped thumb + final Path outerPath = shape!.getOuterPath(_thumbRect!); + canvas.drawPath(outerPath, _paintThumb); + shape!.paint(canvas, _thumbRect!); } - - if (shape == null) { - canvas.drawRect(_thumbRect!, _paintThumb); - return; - } - - final Path outerPath = shape!.getOuterPath(_thumbRect!); - canvas.drawPath(outerPath, _paintThumb); - shape!.paint(canvas, _thumbRect!); } double _thumbExtent() { @@ -573,7 +581,6 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { void paint(Canvas canvas, Size size) { if (_lastAxisDirection == null || _lastMetrics == null - || fadeoutOpacityAnimation.value == 0.0 || _lastMetrics!.maxScrollExtent <= _lastMetrics!.minScrollExtent) return; @@ -606,7 +613,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { /// be used. bool hitTestInteractive(Offset position, PointerDeviceKind kind, { bool forHover = false }) { if (_thumbRect == null) { - // We have never painted the scrollbar, so we do not know where it will be. + // We have not computed the scrollbar position yet. return false; } diff --git a/packages/flutter/test/widgets/scrollbar_test.dart b/packages/flutter/test/widgets/scrollbar_test.dart index 31d20242d83..c84ae71594a 100644 --- a/packages/flutter/test/widgets/scrollbar_test.dart +++ b/packages/flutter/test/widgets/scrollbar_test.dart @@ -883,6 +883,41 @@ void main() { ); }); + testWidgets('Scrollbar will show on hover without needing to scroll first for metrics', (WidgetTester tester) async { + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: MediaQuery( + data: MediaQueryData(), + child: RawScrollbar( + child: SingleChildScrollView( + child: SizedBox(width: 4000.0, height: 4000.0), + ), + ), + ), + ), + ); + await tester.pump(); + + // Hover over scrollbar with mouse. Even though we have not scrolled, the + // ScrollMetricsNotification will have informed the Scrollbar's hit testing. + final TestGesture mouseGesture = await tester.createGesture(kind: ui.PointerDeviceKind.mouse); + await mouseGesture.addPointer(); + addTearDown(mouseGesture.removePointer); + await mouseGesture.moveTo(const Offset(794.0, 5.0)); + await tester.pumpAndSettle(); + // Scrollbar should have appeared in response to hover event. + expect( + find.byType(RawScrollbar), + paints + ..rect(rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0)) + ..rect( + rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0), + color: const Color(0x66BCBCBC), + ), + ); + }); + testWidgets('Scrollbar thumb can be dragged', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); await tester.pumpWidget(