list_view_builder_test.dart 9.38 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';

import 'test_widgets.dart';

void main() {
  testWidgets('ListView.builder mount/dismount smoke test', (WidgetTester tester) async {
12
    final List<int> callbackTracker = <int>[];
13 14 15 16 17

    // the root view is 800x600 in the test environment
    // so if our widget is 100 pixels tall, it should fit exactly 6 times.

    Widget builder() {
18
      return Directionality(
19
        textDirection: TextDirection.ltr,
20 21
        child: FlipWidget(
          left: ListView.builder(
22 23 24
            itemExtent: 100.0,
            itemBuilder: (BuildContext context, int index) {
              callbackTracker.add(index);
25 26
              return Container(
                key: ValueKey<int>(index),
27
                height: 100.0,
28
                child: Text('$index'),
29 30 31 32
              );
            },
          ),
          right: const Text('Not Today'),
33 34 35 36 37 38
        ),
      );
    }

    await tester.pumpWidget(builder());

39
    final FlipWidgetState testWidget = tester.state(find.byType(FlipWidget));
40

41 42 43 44 45
    expect(callbackTracker, equals(<int>[
      0, 1, 2, 3, 4, 5, // visible in viewport
      6, 7, 8, // in caching area
    ]));
    check(visible: <int>[0, 1, 2, 3, 4, 5], hidden: <int>[ 6, 7, 8]);
46 47 48 49 50 51 52 53 54 55 56

    callbackTracker.clear();
    testWidget.flip();
    await tester.pump();

    expect(callbackTracker, equals(<int>[]));

    callbackTracker.clear();
    testWidget.flip();
    await tester.pump();

57 58 59 60 61
    expect(callbackTracker, equals(<int>[
      0, 1, 2, 3, 4, 5,
      6, 7, 8, // in caching area
    ]));
    check(visible: <int>[0, 1, 2, 3, 4, 5], hidden: <int>[ 6, 7, 8]);
62 63 64
  });

  testWidgets('ListView.builder vertical', (WidgetTester tester) async {
65
    final List<int> callbackTracker = <int>[];
66 67 68 69 70

    // the root view is 800x600 in the test environment
    // so if our widget is 200 pixels tall, it should fit exactly 3 times.
    // but if we are offset by 300 pixels, there will be 4, numbered 1-4.

71
    final IndexedWidgetBuilder itemBuilder = (BuildContext context, int index) {
72
      callbackTracker.add(index);
73 74
      return Container(
        key: ValueKey<int>(index),
75 76
        width: 500.0, // this should be ignored
        height: 400.0, // should be overridden by itemExtent
77
        child: Text('$index', textDirection: TextDirection.ltr),
78 79 80
      );
    };

81
    Widget buildWidget() {
82
      return Directionality(
83
        textDirection: TextDirection.ltr,
84 85 86
        child: FlipWidget(
          left: ListView.builder(
            controller: ScrollController(initialScrollOffset: 300.0),
87 88 89
            itemExtent: 200.0,
            itemBuilder: itemBuilder,
          ),
Ian Hickson's avatar
Ian Hickson committed
90
          right: const Text('Not Today'),
91 92 93 94 95
        ),
      );
    }

    void jumpTo(double newScrollOffset) {
Adam Barth's avatar
Adam Barth committed
96
      final ScrollableState scrollable = tester.state(find.byType(Scrollable));
97 98 99 100 101
      scrollable.position.jumpTo(newScrollOffset);
    }

    await tester.pumpWidget(buildWidget());

102 103 104 105 106 107
    expect(callbackTracker, equals(<int>[
      0, // in caching area
      1, 2, 3, 4,
      5, // in caching area
    ]));
    check(visible: <int>[1, 2, 3, 4], hidden: <int>[0, 5]);
108 109 110 111 112 113 114
    callbackTracker.clear();

    jumpTo(400.0);
    // now only 3 should fit, numbered 2-4.

    await tester.pumpWidget(buildWidget());

115 116 117 118 119 120
    expect(callbackTracker, equals(<int>[
      0, 1, // in caching area
      2, 3, 4,
      5, 6, // in caching area
    ]));
    check(visible: <int>[2, 3, 4], hidden: <int>[0, 1, 5, 6]);
121 122 123 124 125 126 127
    callbackTracker.clear();

    jumpTo(500.0);
    // now 4 should fit, numbered 2-5.

    await tester.pumpWidget(buildWidget());

128 129 130 131 132 133
    expect(callbackTracker, equals(<int>[
      0, 1, // in caching area
      2, 3, 4, 5,
      6, // in caching area
    ]));
    check(visible: <int>[2, 3, 4, 5], hidden: <int>[0, 1, 6]);
134 135 136 137
    callbackTracker.clear();
  });

  testWidgets('ListView.builder horizontal', (WidgetTester tester) async {
138
    final List<int> callbackTracker = <int>[];
139 140 141 142 143

    // the root view is 800x600 in the test environment
    // so if our widget is 200 pixels wide, it should fit exactly 4 times.
    // but if we are offset by 300 pixels, there will be 5, numbered 1-5.

144
    final IndexedWidgetBuilder itemBuilder = (BuildContext context, int index) {
145
      callbackTracker.add(index);
146 147
      return Container(
        key: ValueKey<int>(index),
148 149
        width: 400.0, // this should be overridden by itemExtent
        height: 500.0, // this should be ignored
150
        child: Text('$index'),
151 152 153
      );
    };

154
    Widget buildWidget() {
155
      return Directionality(
156
        textDirection: TextDirection.ltr,
157 158 159
        child: FlipWidget(
          left: ListView.builder(
            controller: ScrollController(initialScrollOffset: 300.0),
160 161 162 163 164
            itemBuilder: itemBuilder,
            itemExtent: 200.0,
            scrollDirection: Axis.horizontal,
          ),
          right: const Text('Not Today'),
165 166 167 168 169
        ),
      );
    }

    void jumpTo(double newScrollOffset) {
Adam Barth's avatar
Adam Barth committed
170
      final ScrollableState scrollable = tester.state(find.byType(Scrollable));
171 172 173 174 175
      scrollable.position.jumpTo(newScrollOffset);
    }

    await tester.pumpWidget(buildWidget());

176 177 178 179 180 181
    expect(callbackTracker, equals(<int>[
      0, // in caching area
      1, 2, 3, 4, 5,
      6, // in caching area
    ]));
    check(visible: <int>[1, 2, 3, 4, 5], hidden: <int>[0, 6]);
182 183 184 185 186 187 188
    callbackTracker.clear();

    jumpTo(400.0);
    // now only 4 should fit, numbered 2-5.

    await tester.pumpWidget(buildWidget());

189 190 191 192 193 194
    expect(callbackTracker, equals(<int>[
      0, 1, // in caching area
      2, 3, 4, 5,
      6, 7, // in caching area
    ]));
    check(visible: <int>[2, 3, 4, 5], hidden: <int>[0, 1, 6, 7]);
195 196 197 198 199 200 201
    callbackTracker.clear();

    jumpTo(500.0);
    // now only 5 should fit, numbered 2-6.

    await tester.pumpWidget(buildWidget());

202 203 204 205 206 207
    expect(callbackTracker, equals(<int>[
      0, 1, // in caching area
      2, 3, 4, 5, 6,
      7, // in caching area
    ]));
    check(visible: <int>[2, 3, 4, 5, 6], hidden: <int>[0, 1, 7]);
208 209 210 211
    callbackTracker.clear();
  });

  testWidgets('ListView.builder 10 items, 2-3 items visible', (WidgetTester tester) async {
212
    final List<int> callbackTracker = <int>[];
213 214 215 216 217

    // The root view is 800x600 in the test environment and our list
    // items are 300 tall. Scrolling should cause two or three items
    // to be built.

218
    final IndexedWidgetBuilder itemBuilder = (BuildContext context, int index) {
219
      callbackTracker.add(index);
220
      return Text('$index', key: ValueKey<int>(index), textDirection: TextDirection.ltr);
221 222
    };

223
    final Widget testWidget = Directionality(
224
      textDirection: TextDirection.ltr,
225
      child: ListView.builder(
226 227 228 229
        itemBuilder: itemBuilder,
        itemExtent: 300.0,
        itemCount: 10,
      ),
230 231 232
    );

    void jumpTo(double newScrollOffset) {
Adam Barth's avatar
Adam Barth committed
233
      final ScrollableState scrollable = tester.state(find.byType(Scrollable));
234 235 236 237
      scrollable.position.jumpTo(newScrollOffset);
    }

    await tester.pumpWidget(testWidget);
238 239
    expect(callbackTracker, equals(<int>[0, 1, 2]));
    check(visible: <int>[0, 1], hidden: <int>[2]);
240 241 242 243 244
    callbackTracker.clear();

    jumpTo(150.0);
    await tester.pump();

245 246
    expect(callbackTracker, equals(<int>[3]));
    check(visible: <int>[0, 1, 2], hidden: <int>[3]);
247 248 249 250 251
    callbackTracker.clear();

    jumpTo(600.0);
    await tester.pump();

252 253
    expect(callbackTracker, equals(<int>[4]));
    check(visible: <int>[2, 3], hidden: <int>[0, 1, 4]);
254 255 256 257 258
    callbackTracker.clear();

    jumpTo(750.0);
    await tester.pump();

259 260
    expect(callbackTracker, equals(<int>[5]));
    check(visible: <int>[2, 3, 4], hidden: <int>[0, 1, 5]);
261 262 263
    callbackTracker.clear();
  });

264 265
  testWidgets('ListView.separated', (WidgetTester tester) async {
    Widget buildFrame({ int itemCount }) {
266
      return Directionality(
267
        textDirection: TextDirection.ltr,
268
        child: ListView.separated(
269 270
          itemCount: itemCount,
          itemBuilder: (BuildContext context, int index) {
271
            return SizedBox(
272
              height: 100.0,
273
              child: Text('i$index'),
274 275 276
            );
          },
          separatorBuilder: (BuildContext context, int index) {
277
            return SizedBox(
278
              height: 10.0,
279
              child: Text('s$index'),
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
            );
          },
        ),
      );
    }

    await tester.pumpWidget(buildFrame(itemCount: 0));
    expect(find.text('i0'), findsNothing);
    expect(find.text('s0'), findsNothing);

    await tester.pumpWidget(buildFrame(itemCount: 1));
    expect(find.text('i0'), findsOneWidget);
    expect(find.text('s0'), findsNothing);

    await tester.pumpWidget(buildFrame(itemCount: 2));
    expect(find.text('i0'), findsOneWidget);
    expect(find.text('s0'), findsOneWidget);
    expect(find.text('i1'), findsOneWidget);
    expect(find.text('s1'), findsNothing);

    // ListView's height is 600, so items i0-i5 and s0-s4 fit.
    await tester.pumpWidget(buildFrame(itemCount: 25));
302
    for (String s in <String>['i0', 's0', 'i1', 's1', 'i2', 's2', 'i3', 's3', 'i4', 's4', 'i5'])
303 304 305 306
      expect(find.text(s), findsOneWidget);
    expect(find.text('s5'), findsNothing);
    expect(find.text('i6'), findsNothing);
  });
307
}
308

309
void check({ List<int> visible = const <int>[], List<int> hidden = const <int>[] }) {
310 311 312 313 314 315 316
  for (int i in visible) {
    expect(find.text('$i'), findsOneWidget);
  }
  for (int i in hidden) {
    expect(find.text('$i'), findsNothing);
  }
}