refactor: rely on platform scroll physics and add scroll behavior tests

This commit is contained in:
R-Bharathraj
2026-09-18 17:01:17 +05:30
parent 5122eebc11
commit 4adc21a277
3 changed files with 110 additions and 18 deletions

View File

@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:krow_worker_app/main.dart';
/// A list whose content is shorter than the viewport, so there is nothing to
/// scroll to.
Widget shortList() => MaterialApp(
scrollBehavior: const KrowScrollBehavior(),
home: Scaffold(
body: ListView(
children: const [SizedBox(height: 100, child: Text('only item'))],
),
),
);
Widget longList() => MaterialApp(
scrollBehavior: const KrowScrollBehavior(),
home: Scaffold(
body: ListView(
children: [
for (var i = 0; i < 40; i++) SizedBox(height: 100, child: Text('$i')),
],
),
),
);
ScrollPosition positionOf(WidgetTester tester) =>
tester.state<ScrollableState>(find.byType(Scrollable)).position;
void main() {
// TargetPlatformVariant sets and restores the override around each test;
// assigning it by hand trips the framework's debug-variable invariant check.
final android = TargetPlatformVariant.only(TargetPlatform.android);
final ios = TargetPlatformVariant.only(TargetPlatform.iOS);
group('on Android', () {
testWidgets('content that already fits does not move when dragged', (
tester,
) async {
await tester.pumpWidget(shortList());
final position = positionOf(tester);
// Every vertical list without a controller is handed an
// AlwaysScrollableScrollPhysics by ScrollView itself, so the drag is
// accepted whatever the content. Clamping is what pins it to the
// boundary. Forcing bouncing globally instead let a one-item list be
// dragged hundreds of pixels down, leaving a gap under the header.
await tester.drag(find.text('only item'), const Offset(0, 300));
await tester.pump();
expect(position.pixels, 0);
}, variant: android);
testWidgets('a list that overflows still scrolls', (tester) async {
await tester.pumpWidget(longList());
final position = positionOf(tester);
await tester.drag(find.text('0'), const Offset(0, -200));
await tester.pump();
expect(position.pixels, greaterThan(0));
}, variant: android);
testWidgets('and does not overscroll past the top', (tester) async {
await tester.pumpWidget(longList());
final position = positionOf(tester);
await tester.drag(find.byType(Scrollable), const Offset(0, 400));
await tester.pump();
expect(position.pixels, 0);
}, variant: android);
});
group('on iOS', () {
testWidgets('short content rubber-bands, which is the convention there', (
tester,
) async {
await tester.pumpWidget(shortList());
final position = positionOf(tester);
await tester.drag(find.text('only item'), const Offset(0, 300));
await tester.pump();
expect(position.pixels, lessThan(0));
// And springs back on release, rather than leaving the gap behind.
await tester.pumpAndSettle();
expect(position.pixels, 0);
}, variant: ios);
});
}