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?
ListView is the most commonly used scrolling widget. It displays its children one after another in the scroll direction. In the cross axis, the children are required to fill the ListView. If non-null, the itemExtent forces the children to have the given extent in the scroll direction.
In Flutter, ListView is a scrollable list of widgets arranged linearly. It displays its children one after another in the scroll direction i.e, vertical or horizontal. There are different types of ListViews : ListView. ListView.
The widget test is testing UI components, so it is also called Component Testing. It is used to test a single widget. The main goal of widget testing is to check whether the widget works as expected. A user can write test cases for every widget present in the project.
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
Setting skipOffstate
to false
in your Finder is another approach. Try this:
expect(find.text("Find me!", skipOffstage: false), findsOneWidget);
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With