Unverified Commit 3aed0b67 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Fix scrollbar dragging into overscroll when not allowed (#90634)

parent 37a47015
......@@ -16,6 +16,7 @@ import 'gesture_detector.dart';
import 'media_query.dart';
import 'notification_listener.dart';
import 'primary_scroll_controller.dart';
import 'scroll_configuration.dart';
import 'scroll_controller.dart';
import 'scroll_metrics.dart';
import 'scroll_notification.dart';
......@@ -1367,7 +1368,24 @@ class RawScrollbarState<T extends RawScrollbar> extends State<T> with TickerProv
if (scrollOffsetGlobal != position.pixels) {
// Ensure we don't drag into overscroll if the physics do not allow it.
final double physicsAdjustment = position.physics.applyBoundaryConditions(position, scrollOffsetGlobal);
position.jumpTo(scrollOffsetGlobal - physicsAdjustment);
double newPosition = scrollOffsetGlobal - physicsAdjustment;
// The physics may allow overscroll when actually *scrolling*, but
// dragging on the scrollbar does not always allow us to enter overscroll.
switch(ScrollConfiguration.of(context).getPlatform(context)) {
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
newPosition = newPosition.clamp(0.0, position.maxScrollExtent);
break;
case TargetPlatform.iOS:
case TargetPlatform.android:
// We can only drag the scrollbar into overscroll on mobile
// platforms, and only if the physics allow it.
break;
}
position.jumpTo(newPosition);
}
}
......
......@@ -1025,6 +1025,139 @@ void main() {
);
});
testWidgets('Scrollbar thumb cannot be dragged into overscroll if the platform does not allow it', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: ScrollConfiguration(
// Don't apply a scrollbar automatically for this test.
behavior: const ScrollBehavior().copyWith(
scrollbars: false,
physics: const AlwaysScrollableScrollPhysics(),
),
child: PrimaryScrollController(
controller: scrollController,
child: RawScrollbar(
isAlwaysShown: true,
controller: scrollController,
child: const SingleChildScrollView(
child: SizedBox(width: 4000.0, height: 4000.0),
),
),
),
),
),
),
);
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
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),
),
);
// Try to drag the thumb into overscroll.
const double scrollAmount = -10.0;
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
// The platform drag handling should not have allowed us to enter overscroll.
expect(scrollController.offset, 0.0);
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),
),
);
await dragScrollbarGesture.up();
await tester.pumpAndSettle();
}, variant: const TargetPlatformVariant(<TargetPlatform>{
TargetPlatform.macOS,
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.fuchsia,
}));
testWidgets('Scrollbar thumb can be dragged into overscroll if the platform allows it', (WidgetTester tester) async {
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
child: ScrollConfiguration(
// Don't apply a scrollbar automatically for this test.
behavior: const ScrollBehavior().copyWith(
scrollbars: false,
physics: const AlwaysScrollableScrollPhysics(),
),
child: PrimaryScrollController(
controller: scrollController,
child: RawScrollbar(
isAlwaysShown: true,
controller: scrollController,
child: const SingleChildScrollView(
child: SizedBox(width: 4000.0, height: 4000.0),
),
),
),
),
),
),
);
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
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),
),
);
// Try to drag the thumb into overscroll.
const double scrollAmount = -10.0;
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
// The platform drag handling should have allowed us to enter overscroll.
expect(scrollController.offset, lessThan(-66.0));
expect(
find.byType(RawScrollbar),
paints
..rect(rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0))
..rect(
// The size of the scrollbar thumb shrinks when overscrolling
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 80.0),
color: const Color(0x66BCBCBC),
),
);
await dragScrollbarGesture.up();
await tester.pumpAndSettle();
}, variant: const TargetPlatformVariant(<TargetPlatform>{
TargetPlatform.android,
TargetPlatform.iOS,
}));
// Regression test for https://github.com/flutter/flutter/issues/66444
testWidgets("RawScrollbar doesn't show when scroll the inner scrollable widget", (WidgetTester tester) async {
final GlobalKey key1 = GlobalKey();
......
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