'How to find off-screen ListView child in widget tests?
When displaying multiple children in a ListView, if a child is off-screen it can't be found by a widget test. Here's a full example:
main.dart
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Test()));
}
}
class Test extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
Container(
height: 600,
color: Colors.red,
),
Text("Find me!"),
],
);
}
}
main_test.dart
import 'package:flutter_app/main.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets("Find text", (WidgetTester tester) async {
final testableWidget = App();
await tester.pumpWidget(testableWidget);
expect(find.text("Find me!"), findsOneWidget);
});
}
This test fails, however if I change the height of the Container in main.dart to 599 it works.
Anyone know why this happens? Is it a bug? Is there a way around it?
Solution 1:[1]
Tests should behave as your app would do, otherwise, your tests become useless (since you're not testing the real behavior). As such, this is not a bug.
You have to manually scroll the ListView inside your tests to make it load more widgets.
This can be done using tester:
final gesture = await tester.startGesture(Offset.zero /* THe position of your listview */ );
// Manual scroll
await gesture.moveBy(const Offset(0, 100));
await tester.pump(); // flush the widget tree
Solution 2:[2]
Setting skipOffstate to false in your Finder is another approach. Try this:
expect(find.text("Find me!", skipOffstage: false), findsOneWidget);
Solution 3:[3]
dragUntilVisible helps to scroll Listview or SingleChildScrollView to scroll till the expected widget is visible
final expectedWidget = find.byText("Find me!");
await tester.dragUntilVisible(
expectedWidget, // what you want to find
find.byType(ListView),
// widget you want to scroll
const Offset(0, 500), // delta to move
duration: Duration(seconds: 2));
Solution 4:[4]
I highly recommend you to pay attention in the "Cartesian plane" of your screen/dragging movement.
Let me explain:
- You should use: await tester.drag(keyCartItemProduct1, Offset(-500.0, 0.0));
- However, your "Offset" Command, must obey the same "Cartesian direction" than your Dragging.
2.1) Therefore: (The command Offset uses Cartesian 'directions') - lets see: a) Left Dragging: Offset(-500.0, 0.0) b) Right Dragging: Offset(+500.0, 0.0) c) Up Dragging: Offset(0.0, +500.0) d) Down Dragging: Offset(0.0, -500.0)
Solution 5:[5]
try this code with skipOffstage set to false, it works fine.
testWidgets('Find widget off of screen', (WidgetTester tester) async {
await tester.pumpWidget(yourScreen);
expect(find.byKey(const Key('widgetname'), skipOffstage: false), findsOneWidget); });
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | Jitesh Mohite |
| Solution 4 | GtdDev |
| Solution 5 | Confiance |
