scroll_view_test.dart 13.6 KB
Newer Older
1 2 3 4 5
// Copyright 2017 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';
6
import 'package:flutter/widgets.dart';
7

8
import 'states.dart';
9 10

void main() {
11
  testWidgets('ListView control test', (WidgetTester tester) async {
12
    final List<String> log = <String>[];
13

14
    await tester.pumpWidget(
15
      Directionality(
16
        textDirection: TextDirection.ltr,
17
        child: ListView(
18
          children: kStates.map<Widget>((String state) {
19
            return GestureDetector(
20 21 22
              onTap: () {
                log.add(state);
              },
23
              child: Container(
24 25
                height: 200.0,
                color: const Color(0xFF0000FF),
26
                child: Text(state),
27 28 29 30 31 32
              ),
            );
          }).toList(),
        ),
      ),
    );
33 34 35 36 37 38 39

    await tester.tap(find.text('Alabama'));
    expect(log, equals(<String>['Alabama']));
    log.clear();

    expect(find.text('Nevada'), findsNothing);

40
    await tester.drag(find.text('Alabama'), const Offset(0.0, -4000.0));
41 42 43
    await tester.pump();

    expect(find.text('Alabama'), findsNothing);
44
    expect(tester.getCenter(find.text('Massachusetts')), equals(const Offset(400.0, 100.0)));
45 46 47 48 49

    await tester.tap(find.text('Massachusetts'));
    expect(log, equals(<String>['Massachusetts']));
    log.clear();
  });
50

51 52
  testWidgets('ListView restart ballistic activity out of range', (WidgetTester tester) async {
    Widget buildListView(int n) {
53
      return Directionality(
54
        textDirection: TextDirection.ltr,
55
        child: ListView(
56
          children: kStates.take(n).map<Widget>((String state) {
57
            return Container(
58 59
              height: 200.0,
              color: const Color(0xFF0000FF),
60
              child: Text(state),
61 62 63
            );
          }).toList(),
        ),
64 65 66
      );
    }

67 68 69
    await tester.pumpWidget(buildListView(30));
    await tester.fling(find.byType(ListView), const Offset(0.0, -4000.0), 4000.0);
    await tester.pumpWidget(buildListView(15));
70 71 72 73
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
    await tester.pump(const Duration(milliseconds: 10));
74
    await tester.pumpAndSettle(const Duration(milliseconds: 100));
75

76
    final Viewport viewport = tester.widget(find.byType(Viewport));
77 78
    expect(viewport.offset.pixels, equals(2400.0));
  });
Adam Barth's avatar
Adam Barth committed
79 80

  testWidgets('CustomScrollView control test', (WidgetTester tester) async {
81
    final List<String> log = <String>[];
Adam Barth's avatar
Adam Barth committed
82

83
    await tester.pumpWidget(
84
      Directionality(
85
        textDirection: TextDirection.ltr,
86
        child: CustomScrollView(
87
          slivers: <Widget>[
88 89
            SliverList(
              delegate: SliverChildListDelegate(
90
                kStates.map<Widget>((String state) {
91
                  return GestureDetector(
92 93 94
                    onTap: () {
                      log.add(state);
                    },
95
                    child: Container(
96 97
                      height: 200.0,
                      color: const Color(0xFF0000FF),
98
                      child: Text(state),
99 100 101 102 103 104
                    ),
                  );
                }).toList(),
              ),
            ),
          ],
Adam Barth's avatar
Adam Barth committed
105
        ),
106 107
      ),
    );
Adam Barth's avatar
Adam Barth committed
108 109 110 111 112 113 114

    await tester.tap(find.text('Alabama'));
    expect(log, equals(<String>['Alabama']));
    log.clear();

    expect(find.text('Nevada'), findsNothing);

115
    await tester.drag(find.text('Alabama'), const Offset(0.0, -4000.0));
Adam Barth's avatar
Adam Barth committed
116 117 118
    await tester.pump();

    expect(find.text('Alabama'), findsNothing);
119
    expect(tester.getCenter(find.text('Massachusetts')), equals(const Offset(400.0, 100.0)));
Adam Barth's avatar
Adam Barth committed
120 121 122 123 124

    await tester.tap(find.text('Massachusetts'));
    expect(log, equals(<String>['Massachusetts']));
    log.clear();
  });
125 126 127

  testWidgets('Can jumpTo during drag', (WidgetTester tester) async {
    final List<Type> log = <Type>[];
128
    final ScrollController controller = ScrollController();
129

130
    await tester.pumpWidget(
131
      Directionality(
132
        textDirection: TextDirection.ltr,
133
        child: NotificationListener<ScrollNotification>(
134 135 136 137
          onNotification: (ScrollNotification notification) {
            log.add(notification.runtimeType);
            return false;
          },
138
          child: ListView(
139 140
            controller: controller,
            children: kStates.map<Widget>((String state) {
141
              return Container(
142
                height: 200.0,
143
                child: Text(state),
144 145 146 147
              );
            }).toList(),
          ),
        ),
148
      ),
149
    );
150 151 152

    expect(log, isEmpty);

153
    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    await gesture.moveBy(const Offset(0.0, -100.0));

    expect(log, equals(<Type>[
      ScrollStartNotification,
      UserScrollNotification,
      ScrollUpdateNotification,
    ]));
    log.clear();

    await tester.pump();

    controller.jumpTo(550.0);

    expect(controller.offset, equals(550.0));
    expect(log, equals(<Type>[
      ScrollEndNotification,
      UserScrollNotification,
      ScrollStartNotification,
      ScrollUpdateNotification,
      ScrollEndNotification,
    ]));
    log.clear();

    await tester.pump();
    await gesture.moveBy(const Offset(0.0, -100.0));

    expect(controller.offset, equals(550.0));
    expect(log, isEmpty);
  });
183

184
  testWidgets('Vertical CustomScrollViews are primary by default', (WidgetTester tester) async {
185
    final CustomScrollView view = CustomScrollView(scrollDirection: Axis.vertical);
186 187 188 189
    expect(view.primary, isTrue);
  });

  testWidgets('Vertical ListViews are primary by default', (WidgetTester tester) async {
190
    final ListView view = ListView(scrollDirection: Axis.vertical);
191 192 193 194
    expect(view.primary, isTrue);
  });

  testWidgets('Vertical GridViews are primary by default', (WidgetTester tester) async {
195
    final GridView view = GridView.count(
196 197 198 199 200 201 202
      scrollDirection: Axis.vertical,
      crossAxisCount: 1,
    );
    expect(view.primary, isTrue);
  });

  testWidgets('Horizontal CustomScrollViews are non-primary by default', (WidgetTester tester) async {
203
    final CustomScrollView view = CustomScrollView(scrollDirection: Axis.horizontal);
204 205 206 207
    expect(view.primary, isFalse);
  });

  testWidgets('Horizontal ListViews are non-primary by default', (WidgetTester tester) async {
208
    final ListView view = ListView(scrollDirection: Axis.horizontal);
209 210 211 212
    expect(view.primary, isFalse);
  });

  testWidgets('Horizontal GridViews are non-primary by default', (WidgetTester tester) async {
213
    final GridView view = GridView.count(
214 215 216 217 218 219 220
      scrollDirection: Axis.horizontal,
      crossAxisCount: 1,
    );
    expect(view.primary, isFalse);
  });

  testWidgets('CustomScrollViews with controllers are non-primary by default', (WidgetTester tester) async {
221 222
    final CustomScrollView view = CustomScrollView(
      controller: ScrollController(),
223 224 225 226 227 228
      scrollDirection: Axis.vertical,
    );
    expect(view.primary, isFalse);
  });

  testWidgets('ListViews with controllers are non-primary by default', (WidgetTester tester) async {
229 230
    final ListView view = ListView(
      controller: ScrollController(),
231 232 233 234 235 236
      scrollDirection: Axis.vertical,
    );
    expect(view.primary, isFalse);
  });

  testWidgets('GridViews with controllers are non-primary by default', (WidgetTester tester) async {
237 238
    final GridView view = GridView.count(
      controller: ScrollController(),
239 240 241 242 243 244
      scrollDirection: Axis.vertical,
      crossAxisCount: 1,
    );
    expect(view.primary, isFalse);
  });

245
  testWidgets('CustomScrollView sets PrimaryScrollController when primary', (WidgetTester tester) async {
246
    final ScrollController primaryScrollController = ScrollController();
247
    await tester.pumpWidget(
248
      Directionality(
249
        textDirection: TextDirection.ltr,
250
        child: PrimaryScrollController(
251
          controller: primaryScrollController,
252
          child: CustomScrollView(primary: true),
253 254 255
        ),
      ),
    );
256
    final Scrollable scrollable = tester.widget(find.byType(Scrollable));
257 258 259 260
    expect(scrollable.controller, primaryScrollController);
  });

  testWidgets('ListView sets PrimaryScrollController when primary', (WidgetTester tester) async {
261
    final ScrollController primaryScrollController = ScrollController();
262
    await tester.pumpWidget(
263
      Directionality(
264
        textDirection: TextDirection.ltr,
265
        child: PrimaryScrollController(
266
          controller: primaryScrollController,
267
          child: ListView(primary: true),
268 269 270
        ),
      ),
    );
271
    final Scrollable scrollable = tester.widget(find.byType(Scrollable));
272 273 274 275
    expect(scrollable.controller, primaryScrollController);
  });

  testWidgets('GridView sets PrimaryScrollController when primary', (WidgetTester tester) async {
276
    final ScrollController primaryScrollController = ScrollController();
277
    await tester.pumpWidget(
278
      Directionality(
279
        textDirection: TextDirection.ltr,
280
        child: PrimaryScrollController(
281
          controller: primaryScrollController,
282
          child: GridView.count(primary: true, crossAxisCount: 1),
283 284 285
        ),
      ),
    );
286
    final Scrollable scrollable = tester.widget(find.byType(Scrollable));
287 288
    expect(scrollable.controller, primaryScrollController);
  });
289 290

  testWidgets('Nested scrollables have a null PrimaryScrollController', (WidgetTester tester) async {
291
    const Key innerKey = Key('inner');
292
    final ScrollController primaryScrollController = ScrollController();
293
    await tester.pumpWidget(
294
      Directionality(
295
        textDirection: TextDirection.ltr,
296
        child: PrimaryScrollController(
297
          controller: primaryScrollController,
298
          child: ListView(
299 300
            primary: true,
            children: <Widget>[
301
              Container(
302
                constraints: const BoxConstraints(maxHeight: 200.0),
303
                child: ListView(key: innerKey, primary: true),
304 305
              ),
            ],
306
          ),
307
        ),
308
      ),
309
    );
310

311
    final Scrollable innerScrollable = tester.widget(
312 313 314 315 316 317 318
      find.descendant(
        of: find.byKey(innerKey),
        matching: find.byType(Scrollable),
      ),
    );
    expect(innerScrollable.controller, isNull);
  });
319 320

  testWidgets('Primary ListViews are always scrollable', (WidgetTester tester) async {
321
    final ListView view = ListView(primary: true);
322
    expect(view.physics, isInstanceOf<AlwaysScrollableScrollPhysics>());
323 324 325
  });

  testWidgets('Non-primary ListViews are not always scrollable', (WidgetTester tester) async {
326
    final ListView view = ListView(primary: false);
327
    expect(view.physics, isNot(isInstanceOf<AlwaysScrollableScrollPhysics>()));
328 329 330
  });

  testWidgets('Defaulting-to-primary ListViews are always scrollable', (WidgetTester tester) async {
331
    final ListView view = ListView(scrollDirection: Axis.vertical);
332
    expect(view.physics, isInstanceOf<AlwaysScrollableScrollPhysics>());
333 334 335
  });

  testWidgets('Defaulting-to-not-primary ListViews are not always scrollable', (WidgetTester tester) async {
336
    final ListView view = ListView(scrollDirection: Axis.horizontal);
337
    expect(view.physics, isNot(isInstanceOf<AlwaysScrollableScrollPhysics>()));
338 339 340 341 342
  });

  testWidgets('primary:true leads to scrolling', (WidgetTester tester) async {
    bool scrolled = false;
    await tester.pumpWidget(
343
      Directionality(
344
        textDirection: TextDirection.ltr,
345
        child: NotificationListener<OverscrollNotification>(
346
          onNotification: (OverscrollNotification message) { scrolled = true; return false; },
347
          child: ListView(
348
            primary: true,
349
            children: const <Widget>[],
350
          ),
351 352 353 354 355 356 357 358 359 360
        ),
      ),
    );
    await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
    expect(scrolled, isTrue);
  });

  testWidgets('primary:false leads to no scrolling', (WidgetTester tester) async {
    bool scrolled = false;
    await tester.pumpWidget(
361
      Directionality(
362
        textDirection: TextDirection.ltr,
363
        child: NotificationListener<OverscrollNotification>(
364
          onNotification: (OverscrollNotification message) { scrolled = true; return false; },
365
          child: ListView(
366
            primary: false,
367
            children: const <Widget>[],
368
          ),
369 370 371 372 373 374 375
        ),
      ),
    );
    await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
    expect(scrolled, isFalse);
  });

Ian Hickson's avatar
Ian Hickson committed
376
  testWidgets('physics:AlwaysScrollableScrollPhysics actually overrides primary:false default behavior', (WidgetTester tester) async {
377 378
    bool scrolled = false;
    await tester.pumpWidget(
379
      Directionality(
380
        textDirection: TextDirection.ltr,
381
        child: NotificationListener<OverscrollNotification>(
382
          onNotification: (OverscrollNotification message) { scrolled = true; return false; },
383
          child: ListView(
384 385
            primary: false,
            physics: const AlwaysScrollableScrollPhysics(),
386
            children: const <Widget>[],
387
          ),
388 389 390 391 392 393 394
        ),
      ),
    );
    await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
    expect(scrolled, isTrue);
  });

Ian Hickson's avatar
Ian Hickson committed
395
  testWidgets('physics:ScrollPhysics actually overrides primary:true default behavior', (WidgetTester tester) async {
396 397
    bool scrolled = false;
    await tester.pumpWidget(
398
      Directionality(
399
        textDirection: TextDirection.ltr,
400
        child: NotificationListener<OverscrollNotification>(
401
          onNotification: (OverscrollNotification message) { scrolled = true; return false; },
402
          child: ListView(
403 404
            primary: true,
            physics: const ScrollPhysics(),
405
            children: const <Widget>[],
406
          ),
407 408 409 410 411 412
        ),
      ),
    );
    await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 100.0));
    expect(scrolled, isFalse);
  });
413
}