I am trying to write an integration test for a screen with multiple TextFormFields. For each test, I am simply:
- Tapping the text field
- Entering some text
- Checking if the text exists in the text field.
Using Flutter driver, the following works well for each TextFormField:
final myTextField = find.byValueKey('TextField');
test('Enter text into text field', () async {
await driver.tap(myTextField);
await driver.enterText('Hello world');
await driver.waitFor(find.text('Hello world'));
});
However for any TextFormFields that are off-screen (ie. not visible / outside of the viewport), the test fails with a timeout error.
I tried adding scrollIntoView()
to the tests to tell Flutter Driver to scroll until the field is in the viewport. Below is an example:
final myTextField = find.byValueKey('TextField');
test('Enter text into text field', () async {
await driver.scrollIntoView(myTextField); // Added
await driver.tap(myTextField);
await driver.enterText('Hello world');
await driver.waitFor(find.text('Hello world'));
});
However the tests continue to fail with a timeout error:
FlutterDriver: tap message is taking a long time to complete...
00:39 +0 -1: My screen: Enter text into text field [E]
TimeoutException after 0:00:30.000000: Test timed out after 30 seconds.
How do I test TextFormFields that are outside of the viewport?
Update: I've found that using scrollUntilVisible()
is preferred to scrollIntoView()
, as the element may not yet have been rendered yet and therefore cannot be found (see https://flutter.dev/docs/cookbook/testing/integration/scrolling).
scrollUntilVisible()
successfully auto-scrolls the screen and finds the element that was off screen. However, once found, the screen automatically jumps back up to the top for some reason, making the element off-screen again. What's going on?
final myTextField = find.byValueKey('TextField');
final myListView = find.byValueKey('ListView');
test('Enter text into text field', () async {
await scrollUntilVisible(
myListView,
finder,
// To scroll down the list, provide a negative value to dyScroll.
// Ensure that this value is a small enough increment to
// scroll the item into view without potentially scrolling past it.
//
// To scroll through horizontal lists, provide a dxScroll
// property instead.
dyScroll: -100
);
await driver.tap(myTextField);
await driver.enterText('Hello world');
await driver.waitFor(find.text('Hello world'));
});