mirror of
https://github.com/hanskokx/scroll_forwarder.git
synced 2026-08-12 06:40:52 +02:00
280 lines
8.0 KiB
Dart
280 lines
8.0 KiB
Dart
import "package:flutter/gestures.dart";
|
|
import "package:flutter/material.dart";
|
|
import "package:flutter_test/flutter_test.dart";
|
|
import "package:scroll_forwarder/scroll_forwarder.dart";
|
|
|
|
void main() {
|
|
const double listWidth = 200;
|
|
const double listHeight = 400;
|
|
const Offset margin = Offset(10, listHeight / 2);
|
|
const Offset overList = Offset(400, listHeight / 2);
|
|
|
|
Future<ScrollController> pumpForwarder(
|
|
WidgetTester tester, {
|
|
ScrollPosition? Function()? positionProvider,
|
|
}) async {
|
|
final ScrollController controller = ScrollController();
|
|
addTearDown(controller.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: ScrollForwarder(
|
|
positionProvider:
|
|
positionProvider ??
|
|
() => controller.hasClients ? controller.position : null,
|
|
child: Center(
|
|
child: SizedBox(
|
|
width: listWidth,
|
|
height: listHeight,
|
|
child: ListView(
|
|
controller: controller,
|
|
children: const [
|
|
SizedBox(height: 600),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
return controller;
|
|
}
|
|
|
|
Future<void> scrollAt(
|
|
WidgetTester tester,
|
|
Offset position,
|
|
Offset delta,
|
|
) async {
|
|
final TestPointer pointer = TestPointer(1, PointerDeviceKind.mouse);
|
|
pointer.hover(position);
|
|
await tester.sendEventToBinding(
|
|
PointerScrollEvent(position: position, scrollDelta: delta),
|
|
);
|
|
}
|
|
|
|
group("scroll signals", () {
|
|
testWidgets("scrolls the target when the pointer is outside the list", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
await scrollAt(tester, margin, const Offset(0, 100));
|
|
|
|
expect(controller.offset, 100.0);
|
|
});
|
|
|
|
testWidgets("scrolls once when the pointer is over the list", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
await scrollAt(tester, overList, const Offset(0, 100));
|
|
|
|
expect(controller.offset, 100.0);
|
|
|
|
await scrollAt(tester, overList, const Offset(0, -40));
|
|
|
|
expect(controller.offset, 60.0);
|
|
});
|
|
|
|
testWidgets("ignores signals when no position is available", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(
|
|
tester,
|
|
positionProvider: () => null,
|
|
);
|
|
|
|
await scrollAt(tester, margin, const Offset(0, 100));
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
|
|
testWidgets("ignores horizontal-only scroll signals", (tester) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
await scrollAt(tester, margin, const Offset(50, 0));
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
|
|
testWidgets("ignores non-scroll pointer signals", (tester) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
await tester.sendEventToBinding(
|
|
const PointerScaleEvent(position: margin),
|
|
);
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
|
|
testWidgets("ignores signals when the position has no content dimensions", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = ScrollController();
|
|
addTearDown(controller.dispose);
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: ScrollForwarder(
|
|
positionProvider: () =>
|
|
controller.hasClients ? controller.position : null,
|
|
child: Scrollable(
|
|
controller: controller,
|
|
viewportBuilder: (context, position) => const SizedBox(),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
await scrollAt(tester, margin, const Offset(0, 100));
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
});
|
|
|
|
group("drags", () {
|
|
testWidgets("dragging up on the margin scrolls the target down", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
final TestGesture gesture = await tester.startGesture(margin);
|
|
await gesture.moveBy(const Offset(0, 20));
|
|
await gesture.moveBy(const Offset(0, -60));
|
|
expect(controller.offset, closeTo(60, 10));
|
|
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(controller.offset, greaterThanOrEqualTo(55));
|
|
});
|
|
|
|
testWidgets("dragging down on the margin scrolls the target up", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
controller.jumpTo(120);
|
|
await tester.pump();
|
|
|
|
final TestGesture gesture = await tester.startGesture(margin);
|
|
await gesture.moveBy(const Offset(0, 20));
|
|
await gesture.moveBy(const Offset(0, 100));
|
|
expect(controller.offset, closeTo(0, 10));
|
|
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(controller.offset, inInclusiveRange(0, 20));
|
|
});
|
|
|
|
testWidgets("dragging over the list still scrolls the list once", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
final TestGesture gesture = await tester.startGesture(overList);
|
|
await gesture.moveBy(const Offset(0, 20));
|
|
await gesture.moveBy(const Offset(0, -100));
|
|
expect(controller.offset, closeTo(100, 10));
|
|
|
|
await gesture.up();
|
|
await tester.pump();
|
|
expect(controller.offset, greaterThanOrEqualTo(95));
|
|
});
|
|
|
|
testWidgets("ignores drags when no position is available", (tester) async {
|
|
final ScrollController controller = await pumpForwarder(
|
|
tester,
|
|
positionProvider: () => null,
|
|
);
|
|
|
|
final TestGesture gesture = await tester.startGesture(margin);
|
|
await gesture.moveBy(const Offset(0, 20));
|
|
await gesture.moveBy(const Offset(0, -100));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
|
|
testWidgets("cancels the pending drag when the pointer is released early", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = await pumpForwarder(tester);
|
|
|
|
final TestGesture gesture = await tester.startGesture(margin);
|
|
await gesture.up();
|
|
await tester.pump();
|
|
|
|
expect(controller.offset, 0.0);
|
|
});
|
|
});
|
|
|
|
group("lifecycle", () {
|
|
testWidgets("keeps recognizers when the drag devices are unchanged", (
|
|
tester,
|
|
) async {
|
|
final ScrollController controller = ScrollController();
|
|
addTearDown(controller.dispose);
|
|
|
|
Widget build(ScrollBehavior behavior) {
|
|
return MaterialApp(
|
|
home: Scaffold(
|
|
body: ScrollConfiguration(
|
|
behavior: behavior,
|
|
child: ScrollForwarder(
|
|
positionProvider: () =>
|
|
controller.hasClients ? controller.position : null,
|
|
child: Center(
|
|
child: SizedBox(
|
|
width: listWidth,
|
|
height: listHeight,
|
|
child: ListView(
|
|
controller: controller,
|
|
children: const [
|
|
SizedBox(height: 600),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
build(const _FirstDevicesScrollBehavior()),
|
|
);
|
|
|
|
await tester.pumpWidget(
|
|
build(const _SecondDevicesScrollBehavior()),
|
|
);
|
|
|
|
final TestGesture gesture = await tester.startGesture(margin);
|
|
await gesture.moveBy(const Offset(0, -60));
|
|
expect(controller.offset, closeTo(60, 10));
|
|
await gesture.up();
|
|
await tester.pump();
|
|
});
|
|
});
|
|
}
|
|
|
|
class _FirstDevicesScrollBehavior extends ScrollBehavior {
|
|
const _FirstDevicesScrollBehavior();
|
|
|
|
@override
|
|
Set<PointerDeviceKind> get dragDevices => const {PointerDeviceKind.touch};
|
|
}
|
|
|
|
class _SecondDevicesScrollBehavior extends ScrollBehavior {
|
|
const _SecondDevicesScrollBehavior();
|
|
|
|
@override
|
|
Set<PointerDeviceKind> get dragDevices => const {PointerDeviceKind.touch};
|
|
}
|