Reputation: 1898
I am trying to write an integration test for a screen with multiple TextFormFields. For each test, I am simply:
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'));
});
Upvotes: 4
Views: 4029
Reputation: 11669
I tried to re-create your case and was able to run a driver test successfully that entered text in non-visible textfield on screen using ScrollIntoView()
method. Here's what I did for demo:
main.dart
, had series of textformfields
having unique key
for each, wrapped inside Column
which was wrapped in SingleChildScrollView
:When we scroll down, you'll see the last textfield with hintText
Last TextField
which is not visible when screen launches.
For this setup, wrote two driver tests to tap and enter text as below:
test('Enter text into first text field', () async {
await driver.tap(myTextField);
await driver.enterText('Hello world');
await driver.waitFor(find.text('Hello world'));
});
test('Enter text into last text field', () async {
await driver.scrollIntoView(myTextField21);
await driver.tap(myTextField21);
await driver.enterText('Hello');
await driver.waitFor(find.text('Hello'));
});
Which successfully entered text in last textfield after scrolling into it first.
Hope this helps.
Upvotes: 2