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
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
// @dart = 2.8

7 8 9
import 'dart:math' as math;

import 'package:flutter_test/flutter_test.dart';
10
import 'package:flutter/rendering.dart';
11 12
import 'package:flutter/widgets.dart';

13
Finder findKey(int i) => find.byKey(ValueKey<int>(i));
14

15
Widget buildSingleChildScrollView(Axis scrollDirection, { bool reverse = false }) {
16
  return Directionality(
17
    textDirection: TextDirection.ltr,
18 19
    child: Center(
      child: SizedBox(
20 21
        width: 600.0,
        height: 400.0,
22
        child: SingleChildScrollView(
23 24
          scrollDirection: scrollDirection,
          reverse: reverse,
25
          child: ListBody(
26 27
            mainAxis: scrollDirection,
            children: <Widget>[
28 29 30 31 32 33 34
              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),
35 36 37 38 39 40 41 42
            ],
          ),
        ),
      ),
    ),
  );
}

43
Widget buildListView(Axis scrollDirection, { bool reverse = false, bool shrinkWrap = false }) {
44
  return Directionality(
45
    textDirection: TextDirection.ltr,
46 47
    child: Center(
      child: SizedBox(
48 49
        width: 600.0,
        height: 400.0,
50
        child: ListView(
51 52
          scrollDirection: scrollDirection,
          reverse: reverse,
53
          addSemanticIndexes: false,
54
          shrinkWrap: shrinkWrap,
55
          children: <Widget>[
56 57 58 59 60 61 62
            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),
63 64 65 66 67 68 69
          ],
        ),
      ),
    ),
  );
}

70
void main() {
71

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

76
      await tester.pumpWidget(buildSingleChildScrollView(Axis.vertical));
77

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Adam Barth's avatar
Adam Barth committed
223
      Scrollable.ensureVisible(findContext(0), alignment: 1.0);
224
      await tester.pump();
225
      expect(tester.getTopLeft(findKey(0)).dy, moreOrLessEquals(500.0, epsilon: 0.1));
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 302 303

    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));
    });
304 305
  });

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // 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));
454
      Future<void> prepare(double offset) async {
Adam Barth's avatar
Adam Barth committed
455
        tester.state<ScrollableState>(find.byType(Scrollable)).position.jumpTo(offset);
456 457 458 459
        await tester.pump();
      }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

716
      await tester.pumpWidget(
717
        Directionality(
718
          textDirection: TextDirection.ltr,
719 720
          child: Center(
            child: SizedBox(
721 722
              width: 600.0,
              height: 400.0,
723
              child: Scrollable(
724
                viewportBuilder: (BuildContext context, ViewportOffset offset) {
725
                  return Viewport(
726 727 728
                    offset: offset,
                    center: const ValueKey<String>('center'),
                    slivers: <Widget>[
729 730 731 732 733 734 735 736 737 738 739 740 741
                      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)),
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 798 799

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