Unverified Commit 8a3c09af authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Add scrollbar tests for simultaneous scroll/drag (#79297)

parent ceb0a5fa
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -771,4 +773,108 @@ void main() { ...@@ -771,4 +773,108 @@ void main() {
) )
); );
}); });
testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/70105
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
CupertinoApp(
home: PrimaryScrollController(
controller: scrollController,
child: CupertinoScrollbar(
isAlwaysShown: true,
controller: scrollController,
child: const SingleChildScrollView(
child: SizedBox(width: 4000.0, height: 4000.0)
),
),
),
),
);
const double scrollAmount = 10.0;
await tester.pumpAndSettle();
expect(
find.byType(CupertinoScrollbar),
paints..rrect(
rrect: RRect.fromRectAndRadius(
const Rect.fromLTRB(794.0, 3.0, 797.0, 92.1),
const Radius.circular(1.5),
),
color: _kScrollbarColor.color,
),
);
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(796.0, 50.0));
await tester.pump(_kLongPressDuration);
await tester.pump(_kScrollbarResizeDuration);
// Drag the thumb down to scroll down.
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
expect(scrollController.offset, greaterThan(10.0));
final double previousOffset = scrollController.offset;
expect(
find.byType(CupertinoScrollbar),
paints..rrect(
rrect: RRect.fromRectAndRadius(
const Rect.fromLTRB(789.0, 13.0, 797.0, 102.1),
const Radius.circular(4.0),
),
color: _kScrollbarColor.color,
),
);
// Execute a pointer scroll while dragging (drag gesture has not come up yet)
final TestPointer pointer = TestPointer(1, ui.PointerDeviceKind.mouse);
pointer.hover(const Offset(793.0, 15.0));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar and still hovered over
// the scrollbar should not have changed the scroll offset.
expect(pointer.location, const Offset(793.0, 15.0));
expect(scrollController.offset, previousOffset);
expect(
find.byType(CupertinoScrollbar),
paints..rrect(
rrect: RRect.fromRectAndRadius(
const Rect.fromLTRB(789.0, 13.0, 797.0, 102.1),
const Radius.circular(4.0),
),
color: _kScrollbarColor.color,
),
);
// Drag is still being held, move pointer to be hovering over another area
// of the scrollable (not over the scrollbar) and execute another pointer scroll
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar changed the offset
expect(pointer.location, const Offset(400.0, 300.0));
expect(scrollController.offset, 0.0);
expect(
find.byType(CupertinoScrollbar),
paints..rrect(
rrect: RRect.fromRectAndRadius(
const Rect.fromLTRB(789.0, 3.0, 797.0, 92.1),
const Radius.circular(4.0),
),
color: _kScrollbarColor.color,
),
);
await dragScrollbarGesture.up();
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
expect(
find.byType(CupertinoScrollbar),
paints..rrect(
rrect: RRect.fromRectAndRadius(
const Rect.fromLTRB(794.0, 3.0, 797.0, 92.1),
const Radius.circular(1.5),
),
color: _kScrollbarColor.color,
),
);
});
} }
...@@ -785,7 +785,7 @@ void main() { ...@@ -785,7 +785,7 @@ void main() {
await dragScrollbarGesture.up(); await dragScrollbarGesture.up();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// The view has scrolled more than it would have by a swipe gesture of the // The view has scrolled more than it would have by a swipe pointer of the
// same distance. // same distance.
expect(scrollController.offset, greaterThan(scrollAmount * 2)); expect(scrollController.offset, greaterThan(scrollAmount * 2));
expect( expect(
...@@ -1276,6 +1276,171 @@ void main() { ...@@ -1276,6 +1276,171 @@ void main() {
expect(scrollController.offset, scrollAmount); expect(scrollController.offset, scrollAmount);
}); });
testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/70105
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
MaterialApp(
home: PrimaryScrollController(
controller: scrollController,
child: Scrollbar(
interactive: true,
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(Scrollbar),
paints
..rect(
rect: _kAndroidTrackDimensions,
color: Colors.transparent,
)
..line(
p1: _kTrackBorderPoint1,
p2: _kTrackBorderPoint2,
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: getStartingThumbRect(isAndroid: true),
color: _kAndroidThumbIdleColor,
),
);
// Drag the thumb down to scroll down.
const double scrollAmount = 10.0;
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
expect(
find.byType(Scrollbar),
paints
..rect(
rect: _kAndroidTrackDimensions,
color: Colors.transparent,
)
..line(
p1: _kTrackBorderPoint1,
p2: _kTrackBorderPoint2,
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: getStartingThumbRect(isAndroid: true),
// Drag color
color: const Color(0x99000000),
),
);
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
expect(scrollController.offset, greaterThan(10.0));
final double previousOffset = scrollController.offset;
expect(
find.byType(Scrollbar),
paints
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 600.0),
color: Colors.transparent,
)
..line(
p1: const Offset(796.0, 0.0),
p2: const Offset(796.0, 600.0),
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: const Rect.fromLTRB(796.0, 10.0, 800.0, 100.0),
color: const Color(0x99000000),
),
);
// Execute a pointer scroll while dragging (drag gesture has not come up yet)
final TestPointer pointer = TestPointer(1, ui.PointerDeviceKind.mouse);
pointer.hover(const Offset(798.0, 15.0));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar and still hovered over
// the scrollbar should not have changed the scroll offset.
expect(pointer.location, const Offset(798.0, 15.0));
expect(scrollController.offset, previousOffset);
expect(
find.byType(Scrollbar),
paints
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 600.0),
color: Colors.transparent,
)
..line(
p1: const Offset(796.0, 0.0),
p2: const Offset(796.0, 600.0),
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: const Rect.fromLTRB(796.0, 10.0, 800.0, 100.0),
color: const Color(0x99000000),
),
);
// Drag is still being held, move pointer to be hovering over another area
// of the scrollable (not over the scrollbar) and execute another pointer scroll
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar changed the offset
expect(pointer.location, const Offset(400.0, 300.0));
expect(scrollController.offset, 0.0);
expect(
find.byType(Scrollbar),
paints
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 600.0),
color: Colors.transparent,
)
..line(
p1: const Offset(796.0, 0.0),
p2: const Offset(796.0, 600.0),
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 90.0),
color: const Color(0x99000000),
),
);
await dragScrollbarGesture.up();
await tester.pumpAndSettle();
expect(scrollController.offset, 0.0);
expect(
find.byType(Scrollbar),
paints
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 600.0),
color: Colors.transparent,
)
..line(
p1: const Offset(796.0, 0.0),
p2: const Offset(796.0, 600.0),
strokeWidth: 1.0,
color: Colors.transparent,
)
..rect(
rect: const Rect.fromLTRB(796.0, 0.0, 800.0, 90.0),
color: const Color(0xffbcbcbc),
),
);
});
testWidgets('Scrollbar.isAlwaysShown triggers assertion when multiple ScrollPositions are attached.', (WidgetTester tester) async { testWidgets('Scrollbar.isAlwaysShown triggers assertion when multiple ScrollPositions are attached.', (WidgetTester tester) async {
Widget _getTabContent({ ScrollController? scrollController }) { Widget _getTabContent({ ScrollController? scrollController }) {
return Scrollbar( return Scrollbar(
......
...@@ -984,4 +984,172 @@ void main() { ...@@ -984,4 +984,172 @@ void main() {
contains('The Scrollbar\'s ScrollController has no ScrollPosition attached.'), contains('The Scrollbar\'s ScrollController has no ScrollPosition attached.'),
); );
}); });
testWidgets('Simultaneous dragging and pointer scrolling does not cause a crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/70105
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: MediaQuery(
data: const MediaQueryData(),
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),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0),
color: const Color(0x66bcbcbc),
),
);
// Drag the thumb down to scroll down.
const double scrollAmount = 10.0;
final TestGesture dragScrollbarGesture = await tester.startGesture(const Offset(797.0, 45.0));
await tester.pumpAndSettle();
expect(
find.byType(RawScrollbar),
paints
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0),
// Drag color
color: const Color(0x66bcbcbc),
),
);
await dragScrollbarGesture.moveBy(const Offset(0.0, scrollAmount));
await tester.pumpAndSettle();
expect(scrollController.offset, greaterThan(10.0));
final double previousOffset = scrollController.offset;
expect(
find.byType(RawScrollbar),
paints
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 10.0, 800.0, 100.0),
color: const Color(0x66bcbcbc),
),
);
// Execute a pointer scroll while dragging (drag gesture has not come up yet)
final TestPointer pointer = TestPointer(1, ui.PointerDeviceKind.mouse);
pointer.hover(const Offset(798.0, 15.0));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 20.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar and still hovered over
// the scrollbar should not have changed the scroll offset.
expect(pointer.location, const Offset(798.0, 15.0));
expect(scrollController.offset, previousOffset);
expect(
find.byType(RawScrollbar),
paints
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 10.0, 800.0, 100.0),
color: const Color(0x66bcbcbc),
),
);
// Drag is still being held, move pointer to be hovering over another area
// of the scrollable (not over the scrollbar) and execute another pointer scroll
pointer.hover(tester.getCenter(find.byType(SingleChildScrollView)));
await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, -70.0)));
await tester.pumpAndSettle();
// Scrolling while holding the drag on the scrollbar changed the offset
expect(pointer.location, const Offset(400.0, 300.0));
expect(scrollController.offset, 0.0);
expect(
find.byType(RawScrollbar),
paints
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 600.0),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0),
color: const Color(0x66bcbcbc),
),
);
await dragScrollbarGesture.up();
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),
color: const Color(0x00000000),
)
..line(
p1: const Offset(794.0, 0.0),
p2: const Offset(794.0, 600.0),
strokeWidth: 1.0,
color: const Color(0x00000000),
)
..rect(
rect: const Rect.fromLTRB(794.0, 0.0, 800.0, 90.0),
color: const Color(0x66bcbcbc),
),
);
});
} }
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