mirror of
https://github.com/hanskokx/scroll_forwarder.git
synced 2026-08-12 06:40:52 +02:00
first commit
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
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};
|
||||
}
|
||||
Reference in New Issue
Block a user