ensure_visible_test.dart 29.9 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6
// 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;

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

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
            mainAxis: scrollDirection,
25 26 27 28 29 30 31 32
            children: const <Widget>[
              SizedBox(key: ValueKey<int>(0), width: 200.0, height: 200.0),
              SizedBox(key: ValueKey<int>(1), width: 200.0, height: 200.0),
              SizedBox(key: ValueKey<int>(2), width: 200.0, height: 200.0),
              SizedBox(key: ValueKey<int>(3), width: 200.0, height: 200.0),
              SizedBox(key: ValueKey<int>(4), width: 200.0, height: 200.0),
              SizedBox(key: ValueKey<int>(5), width: 200.0, height: 200.0),
              SizedBox(key: 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 54 55 56 57 58 59 60
          children: const <Widget>[
            SizedBox(key: ValueKey<int>(0), width: 200.0, height: 200.0),
            SizedBox(key: ValueKey<int>(1), width: 200.0, height: 200.0),
            SizedBox(key: ValueKey<int>(2), width: 200.0, height: 200.0),
            SizedBox(key: ValueKey<int>(3), width: 200.0, height: 200.0),
            SizedBox(key: ValueKey<int>(4), width: 200.0, height: 200.0),
            SizedBox(key: ValueKey<int>(5), width: 200.0, height: 200.0),
            SizedBox(key: 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
                  const SizedBox(height: 200.0),
                  const SizedBox(height: 200.0),
                  const SizedBox(height: 200.0),
                  SizedBox(
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
                  const SizedBox(height: 200.0),
                  const SizedBox(height: 200.0),
                  const SizedBox(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

    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(
233
          children: List<SizedBox>.generate(
234
            7,
235
            (int x) => SizedBox(key: ValueKey<String>('$x, $y'), width: 200.0, height: 200.0),
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
          ),
        ),
      );

      await tester.pumpWidget(
        Directionality(
          textDirection: TextDirection.ltr,
          child: Center(
            child: SizedBox(
              width: 600.0,
              height: 400.0,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: SingleChildScrollView(
                  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));
    });
301 302
  });

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    testWidgets('ListView ensureVisible negative child', (WidgetTester tester) async {
      BuildContext findContext(int i) => tester.element(findKey(i));
450
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
451
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
452 453 454 455
        await tester.pump();
      }

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

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

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

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

      await prepare(-225.0);
Adam Barth's avatar
Adam Barth committed
501
      Scrollable.ensureVisible(findContext(2));
502 503
      await tester.pump();
      expect(getOffset(), equals(-400.0));
504
    });
505 506 507

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

712
      await tester.pumpWidget(
713
        Directionality(
714
          textDirection: TextDirection.ltr,
715 716
          child: Center(
            child: SizedBox(
717 718
              width: 600.0,
              height: 400.0,
719
              child: Scrollable(
720
                viewportBuilder: (BuildContext context, ViewportOffset offset) {
721
                  return Viewport(
722 723
                    offset: offset,
                    center: const ValueKey<String>('center'),
724 725 726 727 728 729 730
                    slivers: const <Widget>[
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-6), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-5), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-4), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-3), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-2), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(-1), width: 200.0, height: 200.0)),
731
                      SliverToBoxAdapter(key: ValueKey<String>('center'), child: SizedBox(key: ValueKey<int>(0), width: 200.0, height: 200.0)),
732 733 734 735 736 737
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(1), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(2), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(3), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(4), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(5), width: 200.0, height: 200.0)),
                      SliverToBoxAdapter(child: SizedBox(key: ValueKey<int>(6), width: 200.0, height: 200.0)),
738 739 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

      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));
    });
  });
796
}