14

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?

Jordan Davies
  • 9,925
  • 6
  • 40
  • 51

5 Answers5

9

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 
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
  • 9
    Worth mentioning, you can also drag a widget that is in the list view: `await tester.drag(find.byType(Container), Offset(0.0, -200)); await tester.pump();` – Jordan Davies Feb 12 '19 at 14:31
9

Setting skipOffstate to false in your Finder is an approach. Try this:

expect(find.text("Find me!", skipOffstage: false), findsOneWidget);
Touré Holder
  • 2,768
  • 2
  • 22
  • 20
  • 2
    Suggest to edit: I think that *skipOffstate* is not a parameter anymore of *ensureVisible* method: https://api.flutter.dev/flutter/widgets/Scrollable/ensureVisible.html – Adrien Arcuri Feb 26 '21 at 15:10
  • That `skipOffstage` simplified so many of my tests :D - love it! – Randomize Jun 03 '22 at 11:19
0

I highly recommend you to pay attention in the "Cartesian plane" of your screen/dragging movement.

Let me explain:

  1. You should use: await tester.drag(keyCartItemProduct1, Offset(-500.0, 0.0));
  2. 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)

GtdDev
  • 748
  • 6
  • 14
0

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));
Jitesh Mohite
  • 31,138
  • 12
  • 157
  • 147
0

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); });
Confiance
  • 706
  • 5
  • 7