diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart index cd507fd18b..c5a06043fd 100644 --- a/packages/flutter/lib/src/widgets/scrollbar.dart +++ b/packages/flutter/lib/src/widgets/scrollbar.dart @@ -214,6 +214,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { trackExtent * fractionVisible ); + final double fractionOverscrolled = 1.0 - extentInside / _lastMetrics.viewportDimension; final double safeMinLength = math.min(minLength, trackExtent); final double newMinLength = (beforeExtent > 0 && afterExtent > 0) // Thumb extent is no smaller than minLength if scrolling normally. @@ -229,7 +230,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { // [0.8, 1.0] to [0.0, 1.0], so 0% to 20% of overscroll will produce // values for the thumb that range between minLength and the smallest // possible value, minOverscrollLength. - : safeMinLength * ((fractionVisible - 0.8).clamp(0.0, 0.2) / 0.2); + : safeMinLength * (1.0 - fractionOverscrolled.clamp(0.0, 0.2) / 0.2); // The `thumbExtent` should be no greater than `trackSize`, otherwise // the scrollbar may scroll towards the wrong direction. diff --git a/packages/flutter/test/widgets/scrollbar_test.dart b/packages/flutter/test/widgets/scrollbar_test.dart index b6a85b098f..32869ae753 100644 --- a/packages/flutter/test/widgets/scrollbar_test.dart +++ b/packages/flutter/test/widgets/scrollbar_test.dart @@ -371,6 +371,77 @@ void main() { }); }); + testWidgets('thumb resizes gradually on overscroll', (WidgetTester tester) async { + const EdgeInsets padding = EdgeInsets.fromLTRB(1, 2, 3, 4); + const Size size = Size(60, 300); + final double scrollExtent = size.height * 10; + final ScrollMetrics metrics = defaultMetrics.copyWith( + minScrollExtent: 0, + maxScrollExtent: scrollExtent, + axisDirection: AxisDirection.down, + viewportDimension: size.height, + ); + + const double minOverscrollLength = 8.0; + final ScrollbarPainter p = _buildPainter( + padding: padding, + scrollMetrics: metrics, + minLength: 36.0, + minOverscrollLength: 8.0, + ); + + // No overscroll gives a full sized thumb. + p.update( + metrics.copyWith( + pixels: 0.0, + ), + AxisDirection.down, + ); + p.paint(testCanvas, size); + final double fullThumbExtent = captureRect().height; + expect(fullThumbExtent, greaterThan(_kMinThumbExtent)); + + // Scrolling to the middle also gives a full sized thumb. + p.update( + metrics.copyWith( + pixels: scrollExtent / 2, + ), + AxisDirection.down, + ); + p.paint(testCanvas, size); + expect(captureRect().height, closeTo(fullThumbExtent, .000001)); + + // Scrolling just to the very end also gives a full sized thumb. + p.update( + metrics.copyWith( + pixels: scrollExtent, + ), + AxisDirection.down, + ); + p.paint(testCanvas, size); + expect(captureRect().height, closeTo(fullThumbExtent, .000001)); + + // Scrolling just past the end shrinks the thumb slightly. + p.update( + metrics.copyWith( + pixels: scrollExtent * 1.001, + ), + AxisDirection.down, + ); + p.paint(testCanvas, size); + expect(captureRect().height, closeTo(fullThumbExtent, 2.0)); + + // Scrolling way past the end shrinks the thumb to minimum. + p.update( + metrics.copyWith( + pixels: double.infinity, + ), + AxisDirection.down, + ); + p.paint(testCanvas, size); + expect(captureRect().height, minOverscrollLength); + }); + test('should scroll towards the right direction', () { const Size size = Size(60, 80);