Unverified Commit ace8abf3 authored by Natalie Sampsell's avatar Natalie Sampsell Committed by GitHub

Fixes to CupertinoScrollbar minLength (#20101)

Add minOverscrollLength to make CupertinoScrollbar thumb correct size in long scroll views.
parent 64532336
...@@ -11,7 +11,8 @@ const Color _kScrollbarColor = Color(0x99777777); ...@@ -11,7 +11,8 @@ const Color _kScrollbarColor = Color(0x99777777);
const double _kScrollbarThickness = 2.5; const double _kScrollbarThickness = 2.5;
const double _kScrollbarMainAxisMargin = 4.0; const double _kScrollbarMainAxisMargin = 4.0;
const double _kScrollbarCrossAxisMargin = 2.5; const double _kScrollbarCrossAxisMargin = 2.5;
const double _kScrollbarMinLength = 4.0; const double _kScrollbarMinLength = 36.0;
const double _kScrollbarMinOverscrollLength = 8.0;
const Radius _kScrollbarRadius = Radius.circular(1.25); const Radius _kScrollbarRadius = Radius.circular(1.25);
const Duration _kScrollbarTimeToFade = Duration(milliseconds: 50); const Duration _kScrollbarTimeToFade = Duration(milliseconds: 50);
const Duration _kScrollbarFadeDuration = Duration(milliseconds: 250); const Duration _kScrollbarFadeDuration = Duration(milliseconds: 250);
...@@ -89,6 +90,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv ...@@ -89,6 +90,7 @@ class _CupertinoScrollbarState extends State<CupertinoScrollbar> with TickerProv
crossAxisMargin: _kScrollbarCrossAxisMargin, crossAxisMargin: _kScrollbarCrossAxisMargin,
radius: _kScrollbarRadius, radius: _kScrollbarRadius,
minLength: _kScrollbarMinLength, minLength: _kScrollbarMinLength,
minOverscrollLength: _kScrollbarMinOverscrollLength,
); );
} }
......
...@@ -47,6 +47,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -47,6 +47,7 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
this.crossAxisMargin = 0.0, this.crossAxisMargin = 0.0,
this.radius, this.radius,
this.minLength = _kMinThumbExtent, this.minLength = _kMinThumbExtent,
this.minOverscrollLength = _kMinThumbExtent,
}) : assert(color != null), }) : assert(color != null),
assert(textDirection != null), assert(textDirection != null),
assert(thickness != null), assert(thickness != null),
...@@ -86,10 +87,14 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -86,10 +87,14 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
final Radius radius; final Radius radius;
/// The smallest size the scrollbar can shrink to when the total scrollable /// The smallest size the scrollbar can shrink to when the total scrollable
/// extent is large and the current visible viewport is small. Mustn't be /// extent is large and the current visible viewport is small, and the
/// null. /// viewport is not overscrolled. Mustn't be null.
final double minLength; final double minLength;
/// The smallest size the scrollbar can shrink to when viewport is
/// overscrolled. Mustn't be null.
final double minOverscrollLength;
ScrollMetrics _lastMetrics; ScrollMetrics _lastMetrics;
AxisDirection _lastAxisDirection; AxisDirection _lastAxisDirection;
...@@ -152,13 +157,40 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter { ...@@ -152,13 +157,40 @@ class ScrollbarPainter extends ChangeNotifier implements CustomPainter {
void painter(Canvas canvas, Size size, double thumbOffset, double thumbExtent), void painter(Canvas canvas, Size size, double thumbOffset, double thumbExtent),
) { ) {
// Establish the minimum size possible. // Establish the minimum size possible.
double thumbExtent = math.min(viewport, minLength); double thumbExtent = math.min(viewport, minOverscrollLength);
if (before + inside + after > 0.0) { if (before + inside + after > 0.0) {
// Thumb extent reflects fraction of content visible, as long as this
// isn't less than the absolute minimum size.
final double fractionVisible = inside / (before + inside + after); final double fractionVisible = inside / (before + inside + after);
thumbExtent = math.max( thumbExtent = math.max(
thumbExtent, thumbExtent,
viewport * fractionVisible - 2 * mainAxisMargin, viewport * fractionVisible - 2 * mainAxisMargin,
); );
// Thumb extent is no smaller than minLength if scrolling normally.
if (before != 0.0 && after != 0.0) {
thumbExtent = math.max(
minLength,
thumbExtent,
);
}
// User is overscrolling. Thumb extent can be less than minLength
// but no smaller than minOverscrollLength. We can't use the
// fractionVisible to produce intermediate values between minLength and
// minOverscrollLength when the user is transitioning from regular
// scrolling to overscrolling, so we instead use the percentage of the
// content that is still in the viewport to determine the size of the
// thumb. iOS behavior appears to have the thumb reach its minimum size
// with ~20% of overscroll. We map the percentage of minLength from
// [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.
else {
thumbExtent = math.max(
thumbExtent,
minLength * (((inside / viewport) - 0.8) / 0.2),
);
}
} }
final double fractionPast = before / (before + after); final double fractionPast = before / (before + after);
......
...@@ -42,4 +42,26 @@ void main() { ...@@ -42,4 +42,26 @@ void main() {
color: const Color(0x15777777), color: const Color(0x15777777),
)); ));
}); });
testWidgets('Scrollbar is not smaller than minLength with large scroll views',
(WidgetTester tester) async {
await tester.pumpWidget(new Directionality(
textDirection: TextDirection.ltr,
child: new CupertinoScrollbar(
child: new SingleChildScrollView(
child: const SizedBox(width: 800.0, height: 20000.0),
),
),
));
final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(SingleChildScrollView)));
await gesture.moveBy(const Offset(0.0, -10.0));
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
// Height is 36.0.
final Rect scrollbarRect = Rect.fromLTWH(795.0, 4.28659793814433, 2.5, 36.0);
expect(find.byType(CupertinoScrollbar), paints..rrect(
rrect: RRect.fromRectAndRadius(scrollbarRect, const Radius.circular(1.25)),
));
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment