ensure_visible_test.dart 30.2 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;

import 'package:flutter_test/flutter_test.dart';
8
import 'package:flutter/rendering.dart';
9 10
import 'package:flutter/widgets.dart';

11
Finder findKey(int i) => find.byKey(ValueKey<int>(i));
12

13
Widget buildSingleChildScrollView(Axis scrollDirection, { bool reverse = false }) {
14
  return Directionality(
15
    textDirection: TextDirection.ltr,
16 17
    child: Center(
      child: SizedBox(
18 19
        width: 600.0,
        height: 400.0,
20
        child: SingleChildScrollView(
21 22
          scrollDirection: scrollDirection,
          reverse: reverse,
23
          child: ListBody(
24 25
            mainAxis: scrollDirection,
            children: <Widget>[
26 27 28 29 30 31 32
              Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(2), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(3), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(4), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(5), width: 200.0, height: 200.0),
              Container(key: const ValueKey<int>(6), width: 200.0, height: 200.0),
33 34 35 36 37 38 39 40
            ],
          ),
        ),
      ),
    ),
  );
}

41
Widget buildListView(Axis scrollDirection, { bool reverse = false, bool shrinkWrap = false }) {
42
  return Directionality(
43
    textDirection: TextDirection.ltr,
44 45
    child: Center(
      child: SizedBox(
46 47
        width: 600.0,
        height: 400.0,
48
        child: ListView(
49 50
          scrollDirection: scrollDirection,
          reverse: reverse,
51
          addSemanticIndexes: false,
52
          shrinkWrap: shrinkWrap,
53
          children: <Widget>[
54 55 56 57 58 59 60
            Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(2), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(3), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(4), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(5), width: 200.0, height: 200.0),
            Container(key: const ValueKey<int>(6), width: 200.0, height: 200.0),
61 62 63 64 65 66 67
          ],
        ),
      ),
    ),
  );
}

68
void main() {
69

Josh Soref's avatar
Josh Soref committed
70 71
  group('SingleChildScrollView', () {
    testWidgets('SingleChildScrollView ensureVisible Axis.vertical', (WidgetTester tester) async {
72
      BuildContext findContext(int i) => tester.element(findKey(i));
73

74
      await tester.pumpWidget(buildSingleChildScrollView(Axis.vertical));
75

Adam Barth's avatar
Adam Barth committed
76
      Scrollable.ensureVisible(findContext(3));
77
      await tester.pump();
78
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
79

Adam Barth's avatar
Adam Barth committed
80
      Scrollable.ensureVisible(findContext(6));
81
      await tester.pump();
82
      expect(tester.getTopLeft(findKey(6)).dy, equals(300.0));
83

Adam Barth's avatar
Adam Barth committed
84
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
85
      await tester.pump();
86
      expect(tester.getBottomRight(findKey(4)).dy, equals(500.0));
87

Adam Barth's avatar
Adam Barth committed
88
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
89
      await tester.pump();
90
      expect(tester.getTopLeft(findKey(0)).dy, equals(100.0));
91

Adam Barth's avatar
Adam Barth committed
92
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
93 94
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
95
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
96 97
    });

Josh Soref's avatar
Josh Soref committed
98
    testWidgets('SingleChildScrollView ensureVisible Axis.horizontal', (WidgetTester tester) async {
99 100 101 102
      BuildContext findContext(int i) => tester.element(findKey(i));

      await tester.pumpWidget(buildSingleChildScrollView(Axis.horizontal));

Adam Barth's avatar
Adam Barth committed
103
      Scrollable.ensureVisible(findContext(3));
104
      await tester.pump();
105
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
106

Adam Barth's avatar
Adam Barth committed
107
      Scrollable.ensureVisible(findContext(6));
108
      await tester.pump();
109
      expect(tester.getTopLeft(findKey(6)).dx, equals(500.0));
110

Adam Barth's avatar
Adam Barth committed
111
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
112
      await tester.pump();
113
      expect(tester.getBottomRight(findKey(4)).dx, equals(700.0));
114

Adam Barth's avatar
Adam Barth committed
115
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
116
      await tester.pump();
117
      expect(tester.getTopLeft(findKey(0)).dx, equals(100.0));
118

Adam Barth's avatar
Adam Barth committed
119
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
120 121
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
122
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
123 124
    });

Josh Soref's avatar
Josh Soref committed
125
    testWidgets('SingleChildScrollView ensureVisible Axis.vertical reverse', (WidgetTester tester) async {
126 127 128 129
      BuildContext findContext(int i) => tester.element(findKey(i));

      await tester.pumpWidget(buildSingleChildScrollView(Axis.vertical, reverse: true));

Adam Barth's avatar
Adam Barth committed
130
      Scrollable.ensureVisible(findContext(3));
131
      await tester.pump();
132
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
133

Adam Barth's avatar
Adam Barth committed
134
      Scrollable.ensureVisible(findContext(0));
135
      await tester.pump();
136
      expect(tester.getBottomRight(findKey(0)).dy, equals(300.0));
137

Adam Barth's avatar
Adam Barth committed
138
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
139
      await tester.pump();
140
      expect(tester.getTopLeft(findKey(2)).dy, equals(100.0));
141

Adam Barth's avatar
Adam Barth committed
142
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
143
      await tester.pump();
144
      expect(tester.getBottomRight(findKey(6)).dy, equals(500.0));
145

Adam Barth's avatar
Adam Barth committed
146
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
147 148
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
149
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
150 151
    });

Josh Soref's avatar
Josh Soref committed
152
    testWidgets('SingleChildScrollView ensureVisible Axis.horizontal reverse', (WidgetTester tester) async {
153 154 155 156
      BuildContext findContext(int i) => tester.element(findKey(i));

      await tester.pumpWidget(buildSingleChildScrollView(Axis.horizontal, reverse: true));

Adam Barth's avatar
Adam Barth committed
157
      Scrollable.ensureVisible(findContext(3));
158
      await tester.pump();
159
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
160

Adam Barth's avatar
Adam Barth committed
161
      Scrollable.ensureVisible(findContext(0));
162
      await tester.pump();
163
      expect(tester.getBottomRight(findKey(0)).dx, equals(300.0));
164

Adam Barth's avatar
Adam Barth committed
165
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
166
      await tester.pump();
167
      expect(tester.getTopLeft(findKey(2)).dx, equals(100.0));
168

Adam Barth's avatar
Adam Barth committed
169
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
170
      await tester.pump();
171
      expect(tester.getBottomRight(findKey(6)).dx, equals(700.0));
172

Adam Barth's avatar
Adam Barth committed
173
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
174 175
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
176
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
177 178
    });

Josh Soref's avatar
Josh Soref committed
179
    testWidgets('SingleChildScrollView ensureVisible rotated child', (WidgetTester tester) async {
180 181 182
      BuildContext findContext(int i) => tester.element(findKey(i));

      await tester.pumpWidget(
183 184
        Center(
          child: SizedBox(
185 186
            width: 600.0,
            height: 400.0,
187 188
            child: SingleChildScrollView(
              child: ListBody(
189
                children: <Widget>[
190 191 192 193
                  Container(height: 200.0),
                  Container(height: 200.0),
                  Container(height: 200.0),
                  Container(
194
                    height: 200.0,
195 196 197 198
                    child: Center(
                      child: Transform(
                        transform: Matrix4.rotationZ(math.pi),
                        child: Container(
199
                          key: const ValueKey<int>(0),
200 201
                          width: 100.0,
                          height: 100.0,
202
                          color: const Color(0xFFFFFFFF),
203 204 205 206
                        ),
                      ),
                    ),
                  ),
207 208 209
                  Container(height: 200.0),
                  Container(height: 200.0),
                  Container(height: 200.0),
210 211 212 213
                ],
              ),
            ),
          ),
214
        ),
215
      );
216

Adam Barth's avatar
Adam Barth committed
217
      Scrollable.ensureVisible(findContext(0));
218
      await tester.pump();
219
      expect(tester.getBottomRight(findKey(0)).dy, moreOrLessEquals(100.0, epsilon: 0.1));
220

Adam Barth's avatar
Adam Barth committed
221
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
222
      await tester.pump();
223
      expect(tester.getTopLeft(findKey(0)).dy, moreOrLessEquals(500.0, epsilon: 0.1));
224
    });
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    testWidgets('Nested SingleChildScrollView ensureVisible behavior test', (WidgetTester tester) async {
      // Regressing test for https://github.com/flutter/flutter/issues/65100
      Finder findKey(String coordinate) => find.byKey(ValueKey<String>(coordinate));
      BuildContext findContext(String coordinate) => tester.element(findKey(coordinate));
      final List<Row> rows = List<Row>.generate(
        7,
        (int y) => Row(
          children: List<Container>.generate(
            7,
            (int x) => Container(key: ValueKey<String>('$x, $y'), width: 200.0, height: 200.0,),
          ),
        ),
      );

      await tester.pumpWidget(
        Directionality(
          textDirection: TextDirection.ltr,
          child: Center(
            child: SizedBox(
              width: 600.0,
              height: 400.0,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: SingleChildScrollView(
                  scrollDirection: Axis.vertical,
                  child: Column(
                    children: rows,
                  ),
                ),
              ),
            ),
          ),
        ),
      );

      //      Items: 7 * 7 Container(width: 200.0, height: 200.0)
      //      viewport: Size(width: 600.0, height: 400.0)
      //
      //               0                       600
      //                 +----------------------+
      //                 |0,0    |1,0    |2,0   |
      //                 |       |       |      |
      //                 +----------------------+
      //                 |0,1    |1,1    |2,1   |
      //                 |       |       |      |
      //             400 +----------------------+

      Scrollable.ensureVisible(findContext('0, 0'));
      await tester.pump();
      expect(tester.getTopLeft(findKey('0, 0')), const Offset(100.0, 100.0));

      Scrollable.ensureVisible(findContext('3, 0'));
      await tester.pump();
      expect(tester.getTopLeft(findKey('3, 0')), const Offset(100.0, 100.0));

      Scrollable.ensureVisible(findContext('3, 0'), alignment: 0.5);
      await tester.pump();
      expect(tester.getTopLeft(findKey('3, 0')), const Offset(300.0, 100.0));

      Scrollable.ensureVisible(findContext('6, 0'));
      await tester.pump();
      expect(tester.getTopLeft(findKey('6, 0')), const Offset(500.0, 100.0));

      Scrollable.ensureVisible(findContext('0, 2'));
      await tester.pump();
      expect(tester.getTopLeft(findKey('0, 2')), const Offset(100.0, 100.0));

      Scrollable.ensureVisible(findContext('3, 2'));
      await tester.pump();
      expect(tester.getTopLeft(findKey('3, 2')), const Offset(100.0, 100.0));

      // It should be at the center of the screen.
      Scrollable.ensureVisible(findContext('3, 2'), alignment: 0.5);
      await tester.pump();
      expect(tester.getTopLeft(findKey('3, 2')), const Offset(300.0, 200.0));
    });
302 303
  });

304 305 306
  group('ListView', () {
    testWidgets('ListView ensureVisible Axis.vertical', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
307
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
308
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
309 310 311 312 313 314
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.vertical));

      await prepare(480.0);
Adam Barth's avatar
Adam Barth committed
315
      Scrollable.ensureVisible(findContext(3));
316
      await tester.pump();
317
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
318 319

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
320
      Scrollable.ensureVisible(findContext(6));
321
      await tester.pump();
322
      expect(tester.getTopLeft(findKey(6)).dy, equals(300.0));
323 324

      await prepare(735.0);
Adam Barth's avatar
Adam Barth committed
325
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
326
      await tester.pump();
327
      expect(tester.getBottomRight(findKey(4)).dy, equals(500.0));
328 329

      await prepare(123.0);
Adam Barth's avatar
Adam Barth committed
330
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
331
      await tester.pump();
332
      expect(tester.getTopLeft(findKey(0)).dy, equals(100.0));
333 334

      await prepare(523.0);
Adam Barth's avatar
Adam Barth committed
335
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
336 337
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
338
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
339 340 341 342
    });

    testWidgets('ListView ensureVisible Axis.horizontal', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
343
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
344
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
345 346 347 348 349 350
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.horizontal));

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
351
      Scrollable.ensureVisible(findContext(3));
352
      await tester.pump();
353
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
354 355

      await prepare(843.0);
Adam Barth's avatar
Adam Barth committed
356
      Scrollable.ensureVisible(findContext(6));
357
      await tester.pump();
358
      expect(tester.getTopLeft(findKey(6)).dx, equals(500.0));
359 360

      await prepare(415.0);
Adam Barth's avatar
Adam Barth committed
361
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
362
      await tester.pump();
363
      expect(tester.getBottomRight(findKey(4)).dx, equals(700.0));
364 365

      await prepare(46.0);
Adam Barth's avatar
Adam Barth committed
366
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
367
      await tester.pump();
368
      expect(tester.getTopLeft(findKey(0)).dx, equals(100.0));
369 370

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
371
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
372 373
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
374
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
375 376 377 378
    });

    testWidgets('ListView ensureVisible Axis.vertical reverse', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
379
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
380
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
381 382 383 384 385 386
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.vertical, reverse: true));

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
387
      Scrollable.ensureVisible(findContext(3));
388
      await tester.pump();
389
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
390 391

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
392
      Scrollable.ensureVisible(findContext(0));
393
      await tester.pump();
394
      expect(tester.getBottomRight(findKey(0)).dy, equals(500.0));
395 396

      await prepare(230.0);
Adam Barth's avatar
Adam Barth committed
397
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
398
      await tester.pump();
399
      expect(tester.getTopLeft(findKey(2)).dy, equals(100.0));
400 401

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
402
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
403
      await tester.pump();
404
      expect(tester.getBottomRight(findKey(6)).dy, equals(300.0));
405 406

      await prepare(345.0);
Adam Barth's avatar
Adam Barth committed
407
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
408 409
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
410
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
411 412 413 414
    });

    testWidgets('ListView ensureVisible Axis.horizontal reverse', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
415
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
416
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
417 418 419 420 421 422
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.horizontal, reverse: true));

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
423
      Scrollable.ensureVisible(findContext(3));
424
      await tester.pump();
425
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
426 427

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
428
      Scrollable.ensureVisible(findContext(0));
429
      await tester.pump();
430
      expect(tester.getBottomRight(findKey(0)).dx, equals(700.0));
431 432

      await prepare(230.0);
Adam Barth's avatar
Adam Barth committed
433
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
434
      await tester.pump();
435
      expect(tester.getTopLeft(findKey(2)).dx, equals(100.0));
436 437

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
438
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
439
      await tester.pump();
440
      expect(tester.getBottomRight(findKey(6)).dx, equals(300.0));
441 442

      await prepare(345.0);
Adam Barth's avatar
Adam Barth committed
443
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
444 445
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
446
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
447 448 449 450 451
    });

    // TODO(abarth): Unskip this test. See https://github.com/flutter/flutter/issues/7919
    testWidgets('ListView ensureVisible negative child', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
452
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
453
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
454 455 456 457
        await tester.pump();
      }

      double getOffset() {
Adam Barth's avatar
Adam Barth committed
458
        return tester.state<ScrollableState>(find.byType(Scrollable)).position.pixels;
459 460 461
      }

      Widget buildSliver(int i) {
462 463
        return SliverToBoxAdapter(
          key: ValueKey<int>(i),
464
          child: const SizedBox(width: 200.0, height: 200.0),
465 466 467
        );
      }

468
      await tester.pumpWidget(
469
        Directionality(
470
          textDirection: TextDirection.ltr,
471 472
          child: Center(
            child: SizedBox(
473 474
              width: 600.0,
              height: 400.0,
475
              child: Scrollable(
476
                viewportBuilder: (BuildContext context, ViewportOffset offset) {
477
                  return Viewport(
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
                    offset: offset,
                    center: const ValueKey<int>(4),
                    slivers: <Widget>[
                      buildSliver(0),
                      buildSliver(1),
                      buildSliver(2),
                      buildSliver(3),
                      buildSliver(4),
                      buildSliver(5),
                      buildSliver(6),
                    ],
                  );
                },
              ),
            ),
493 494
          ),
        ),
495
      );
496 497

      await prepare(-125.0);
Adam Barth's avatar
Adam Barth committed
498
      Scrollable.ensureVisible(findContext(3));
499 500 501 502
      await tester.pump();
      expect(getOffset(), equals(-200.0));

      await prepare(-225.0);
Adam Barth's avatar
Adam Barth committed
503
      Scrollable.ensureVisible(findContext(2));
504 505
      await tester.pump();
      expect(getOffset(), equals(-400.0));
506
    }, skip: true); // https://github.com/flutter/flutter/issues/7919
507 508 509

    testWidgets('ListView ensureVisible rotated child', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
510
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
511
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
512 513 514
        await tester.pump();
      }

515
      await tester.pumpWidget(Directionality(
516
        textDirection: TextDirection.ltr,
517 518
        child: Center(
          child: SizedBox(
519 520
            width: 600.0,
            height: 400.0,
521
            child: ListView(
522
              children: <Widget>[
523 524 525 526
                Container(height: 200.0),
                Container(height: 200.0),
                Container(height: 200.0),
                Container(
527
                  height: 200.0,
528 529 530 531
                  child: Center(
                    child: Transform(
                      transform: Matrix4.rotationZ(math.pi),
                      child: Container(
532
                        key: const ValueKey<int>(0),
533 534
                        width: 100.0,
                        height: 100.0,
535
                        color: const Color(0xFFFFFFFF),
536 537 538 539
                      ),
                    ),
                  ),
                ),
540 541 542
                Container(height: 200.0),
                Container(height: 200.0),
                Container(height: 200.0),
543 544 545
              ],
            ),
          ),
546
        ),
547
      ));
548 549

      await prepare(321.0);
Adam Barth's avatar
Adam Barth committed
550
      Scrollable.ensureVisible(findContext(0));
551
      await tester.pump();
552
      expect(tester.getBottomRight(findKey(0)).dy, moreOrLessEquals(100.0, epsilon: 0.1));
553

Adam Barth's avatar
Adam Barth committed
554
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
555
      await tester.pump();
556
      expect(tester.getTopLeft(findKey(0)).dy, moreOrLessEquals(500.0, epsilon: 0.1));
557
    });
558 559
  });

560 561 562
  group('ListView shrinkWrap', () {
    testWidgets('ListView ensureVisible Axis.vertical', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
563
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
564
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
565 566 567 568 569 570
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.vertical, shrinkWrap: true));

      await prepare(480.0);
Adam Barth's avatar
Adam Barth committed
571
      Scrollable.ensureVisible(findContext(3));
572
      await tester.pump();
573
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
574 575

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
576
      Scrollable.ensureVisible(findContext(6));
577
      await tester.pump();
578
      expect(tester.getTopLeft(findKey(6)).dy, equals(300.0));
579 580

      await prepare(735.0);
Adam Barth's avatar
Adam Barth committed
581
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
582
      await tester.pump();
583
      expect(tester.getBottomRight(findKey(4)).dy, equals(500.0));
584 585

      await prepare(123.0);
Adam Barth's avatar
Adam Barth committed
586
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
587
      await tester.pump();
588
      expect(tester.getTopLeft(findKey(0)).dy, equals(100.0));
589 590

      await prepare(523.0);
Adam Barth's avatar
Adam Barth committed
591
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
592 593
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
594
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));
595 596 597 598
    });

    testWidgets('ListView ensureVisible Axis.horizontal', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
599
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
600
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
601 602 603 604 605 606
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.horizontal, shrinkWrap: true));

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
607
      Scrollable.ensureVisible(findContext(3));
608
      await tester.pump();
609
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
610 611

      await prepare(843.0);
Adam Barth's avatar
Adam Barth committed
612
      Scrollable.ensureVisible(findContext(6));
613
      await tester.pump();
614
      expect(tester.getTopLeft(findKey(6)).dx, equals(500.0));
615 616

      await prepare(415.0);
Adam Barth's avatar
Adam Barth committed
617
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
618
      await tester.pump();
619
      expect(tester.getBottomRight(findKey(4)).dx, equals(700.0));
620 621

      await prepare(46.0);
Adam Barth's avatar
Adam Barth committed
622
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
623
      await tester.pump();
624
      expect(tester.getTopLeft(findKey(0)).dx, equals(100.0));
625 626

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
627
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
628 629
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
630
      expect(tester.getTopLeft(findKey(3)).dx, equals(100.0));
631 632 633 634
    });

    testWidgets('ListView ensureVisible Axis.vertical reverse', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
635
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
636
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
637 638 639 640 641 642
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.vertical, reverse: true, shrinkWrap: true));

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
643
      Scrollable.ensureVisible(findContext(3));
644
      await tester.pump();
645
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
646 647

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
648
      Scrollable.ensureVisible(findContext(0));
649
      await tester.pump();
650
      expect(tester.getBottomRight(findKey(0)).dy, equals(500.0));
651 652

      await prepare(230.0);
Adam Barth's avatar
Adam Barth committed
653
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
654
      await tester.pump();
655
      expect(tester.getTopLeft(findKey(2)).dy, equals(100.0));
656 657

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
658
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
659
      await tester.pump();
660
      expect(tester.getBottomRight(findKey(6)).dy, equals(300.0));
661 662

      await prepare(345.0);
Adam Barth's avatar
Adam Barth committed
663
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
664 665
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
666
      expect(tester.getBottomRight(findKey(3)).dy, equals(500.0));
667 668 669 670
    });

    testWidgets('ListView ensureVisible Axis.horizontal reverse', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
671
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
672
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
673 674 675 676 677 678
        await tester.pump();
      }

      await tester.pumpWidget(buildListView(Axis.horizontal, reverse: true, shrinkWrap: true));

      await prepare(211.0);
Adam Barth's avatar
Adam Barth committed
679
      Scrollable.ensureVisible(findContext(3));
680
      await tester.pump();
681
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
682 683

      await prepare(23.0);
Adam Barth's avatar
Adam Barth committed
684
      Scrollable.ensureVisible(findContext(0));
685
      await tester.pump();
686
      expect(tester.getBottomRight(findKey(0)).dx, equals(700.0));
687 688

      await prepare(230.0);
Adam Barth's avatar
Adam Barth committed
689
      Scrollable.ensureVisible(findContext(2), alignment: 1.0);
690
      await tester.pump();
691
      expect(tester.getTopLeft(findKey(2)).dx, equals(100.0));
692 693

      await prepare(1083.0);
Adam Barth's avatar
Adam Barth committed
694
      Scrollable.ensureVisible(findContext(6), alignment: 1.0);
695
      await tester.pump();
696
      expect(tester.getBottomRight(findKey(6)).dx, equals(300.0));
697 698

      await prepare(345.0);
Adam Barth's avatar
Adam Barth committed
699
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
700 701
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
702
      expect(tester.getBottomRight(findKey(3)).dx, equals(700.0));
703 704
    });
  });
705 706 707 708

  group('Scrollable with center', () {
    testWidgets('ensureVisible', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
709
      Future<void> prepare(double offset) async {
710 711 712 713
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
        await tester.pump();
      }

714
      await tester.pumpWidget(
715
        Directionality(
716
          textDirection: TextDirection.ltr,
717 718
          child: Center(
            child: SizedBox(
719 720
              width: 600.0,
              height: 400.0,
721
              child: Scrollable(
722
                viewportBuilder: (BuildContext context, ViewportOffset offset) {
723
                  return Viewport(
724 725 726
                    offset: offset,
                    center: const ValueKey<String>('center'),
                    slivers: <Widget>[
727 728 729 730 731 732 733 734 735 736 737 738 739
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-6), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-5), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-4), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-3), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-2), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(-1), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(0), width: 200.0, height: 200.0), key: const ValueKey<String>('center')),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(1), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(2), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(3), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(4), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(5), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: Container(key: const ValueKey<int>(6), width: 200.0, height: 200.0)),
740 741 742 743 744
                    ],
                  );
                },
              ),
            ),
745 746
          ),
        ),
747
      );
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797

      await prepare(480.0);
      Scrollable.ensureVisible(findContext(3));
      await tester.pump();
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));

      await prepare(1083.0);
      Scrollable.ensureVisible(findContext(6));
      await tester.pump();
      expect(tester.getTopLeft(findKey(6)).dy, equals(300.0));

      await prepare(735.0);
      Scrollable.ensureVisible(findContext(4), alignment: 1.0);
      await tester.pump();
      expect(tester.getBottomRight(findKey(4)).dy, equals(500.0));

      await prepare(123.0);
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
      await tester.pump();
      expect(tester.getBottomRight(findKey(0)).dy, equals(500.0));

      await prepare(523.0);
      Scrollable.ensureVisible(findContext(3), duration: const Duration(seconds: 1));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
      expect(tester.getTopLeft(findKey(3)).dy, equals(100.0));


      await prepare(-480.0);
      Scrollable.ensureVisible(findContext(-3));
      await tester.pump();
      expect(tester.getTopLeft(findKey(-3)).dy, equals(100.0));

      await prepare(-1083.0);
      Scrollable.ensureVisible(findContext(-6));
      await tester.pump();
      expect(tester.getTopLeft(findKey(-6)).dy, equals(100.0));

      await prepare(-735.0);
      Scrollable.ensureVisible(findContext(-4), alignment: 1.0);
      await tester.pump();
      expect(tester.getBottomRight(findKey(-4)).dy, equals(500.0));

      await prepare(-523.0);
      Scrollable.ensureVisible(findContext(-3), duration: const Duration(seconds: 1));
      await tester.pump();
      await tester.pump(const Duration(milliseconds: 1020));
      expect(tester.getTopLeft(findKey(-3)).dy, equals(100.0));
    });
  });
798
}