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

5
import 'package:flutter/rendering.dart';
6
import 'package:flutter/widgets.dart';
7
import 'package:flutter_test/flutter_test.dart';
8

9
import '../rendering/rendering_tester.dart' show TestCallbackPainter;
10

11 12 13 14
class TestPaintingContext implements PaintingContext {
  final List<Invocation> invocations = <Invocation>[];

  @override
15 16 17
  void noSuchMethod(Invocation invocation) {
    invocations.add(invocation);
  }
18 19
}

20
void main() {
21
  testWidgets('Can construct an empty Stack', (WidgetTester tester) async {
22
    await tester.pumpWidget(
23
      const Directionality(
24
        textDirection: TextDirection.ltr,
25
        child: Stack(),
26 27
      ),
    );
Hans Muller's avatar
Hans Muller committed
28 29
  });

30
  testWidgets('Can construct an empty Centered Stack', (WidgetTester tester) async {
31
    await tester.pumpWidget(
32
      const Directionality(
33
        textDirection: TextDirection.ltr,
34
        child: Center(child: Stack()),
35 36
      ),
    );
37 38
  });

39
  testWidgets('Can change position data', (WidgetTester tester) async {
40
    const Key key = Key('container');
41

42
    await tester.pumpWidget(
43
      const Stack(
44
        alignment: Alignment.topLeft,
45
        children: <Widget>[
46
          Positioned(
47
            left: 10.0,
48
            child: SizedBox(
49 50
              key: key,
              width: 10.0,
51 52 53 54 55
              height: 10.0,
            ),
          ),
        ],
      ),
56 57 58 59 60 61
    );

    Element container;
    StackParentData parentData;

    container = tester.element(find.byKey(key));
62
    parentData = container.renderObject!.parentData! as StackParentData;
63 64 65 66 67 68 69
    expect(parentData.top, isNull);
    expect(parentData.right, isNull);
    expect(parentData.bottom, isNull);
    expect(parentData.left, equals(10.0));
    expect(parentData.width, isNull);
    expect(parentData.height, isNull);

70
    await tester.pumpWidget(
71
      const Stack(
72
        alignment: Alignment.topLeft,
73
        children: <Widget>[
74
          Positioned(
75
            right: 10.0,
76
            child: SizedBox(
77 78
              key: key,
              width: 10.0,
79 80 81 82 83
              height: 10.0,
            ),
          ),
        ],
      ),
84 85 86
    );

    container = tester.element(find.byKey(key));
87
    parentData = container.renderObject!.parentData! as StackParentData;
88 89 90 91 92 93
    expect(parentData.top, isNull);
    expect(parentData.right, equals(10.0));
    expect(parentData.bottom, isNull);
    expect(parentData.left, isNull);
    expect(parentData.width, isNull);
    expect(parentData.height, isNull);
94
  });
95

96
  testWidgets('Can remove parent data', (WidgetTester tester) async {
97
    const Key key = Key('container');
98
    const SizedBox sizedBox = SizedBox(key: key, width: 10.0, height: 10.0);
99

100
    await tester.pumpWidget(
101
      const Stack(
102
        textDirection: TextDirection.ltr,
103
        children: <Widget>[ Positioned(left: 10.0, child: sizedBox) ],
104 105
      ),
    );
106 107 108
    Element containerElement = tester.element(find.byKey(key));

    StackParentData parentData;
109
    parentData = containerElement.renderObject!.parentData! as StackParentData;
110 111 112 113 114 115 116
    expect(parentData.top, isNull);
    expect(parentData.right, isNull);
    expect(parentData.bottom, isNull);
    expect(parentData.left, equals(10.0));
    expect(parentData.width, isNull);
    expect(parentData.height, isNull);

117
    await tester.pumpWidget(
118
      const Stack(
119
        textDirection: TextDirection.ltr,
120
        children: <Widget>[ sizedBox ],
121 122
      ),
    );
123 124
    containerElement = tester.element(find.byKey(key));

125
    parentData = containerElement.renderObject!.parentData! as StackParentData;
126 127 128 129 130 131
    expect(parentData.top, isNull);
    expect(parentData.right, isNull);
    expect(parentData.bottom, isNull);
    expect(parentData.left, isNull);
    expect(parentData.width, isNull);
    expect(parentData.height, isNull);
132
  });
Hans Muller's avatar
Hans Muller committed
133

134
  testWidgets('Can align non-positioned children (LTR)', (WidgetTester tester) async {
135 136
    const Key child0Key = Key('child0');
    const Key child1Key = Key('child1');
137

138
    await tester.pumpWidget(
139
      const Directionality(
140
        textDirection: TextDirection.ltr,
141 142
        child: Center(
          child: Stack(
143
            alignment: Alignment.center,
144
            children: <Widget>[
145 146
              SizedBox(key: child0Key, width: 20.0, height: 20.0),
              SizedBox(key: child1Key, width: 10.0, height: 10.0),
147 148 149 150
            ],
          ),
        ),
      ),
151
    );
Hans Muller's avatar
Hans Muller committed
152

153
    final Element child0 = tester.element(find.byKey(child0Key));
154
    final StackParentData child0RenderObjectParentData = child0.renderObject!.parentData! as StackParentData;
155
    expect(child0RenderObjectParentData.offset, equals(Offset.zero));
Hans Muller's avatar
Hans Muller committed
156

157
    final Element child1 = tester.element(find.byKey(child1Key));
158
    final StackParentData child1RenderObjectParentData = child1.renderObject!.parentData! as StackParentData;
159
    expect(child1RenderObjectParentData.offset, equals(const Offset(5.0, 5.0)));
160 161

    await tester.pumpWidget(
162
      const Directionality(
163
        textDirection: TextDirection.ltr,
164 165
        child: Center(
          child: Stack(
166
            alignment: AlignmentDirectional.bottomEnd,
167
            children: <Widget>[
168 169
              SizedBox(key: child0Key, width: 20.0, height: 20.0),
              SizedBox(key: child1Key, width: 10.0, height: 10.0),
170 171 172 173 174 175
            ],
          ),
        ),
      ),
    );

176
    expect(child0RenderObjectParentData.offset, equals(Offset.zero));
177 178 179 180
    expect(child1RenderObjectParentData.offset, equals(const Offset(10.0, 10.0)));
  });

  testWidgets('Can align non-positioned children (RTL)', (WidgetTester tester) async {
181 182
    const Key child0Key = Key('child0');
    const Key child1Key = Key('child1');
183 184

    await tester.pumpWidget(
185
      const Directionality(
186
        textDirection: TextDirection.rtl,
187 188
        child: Center(
          child: Stack(
189
            alignment: Alignment.center,
190
            children: <Widget>[
191 192
              SizedBox(key: child0Key, width: 20.0, height: 20.0),
              SizedBox(key: child1Key, width: 10.0, height: 10.0),
193 194 195 196 197 198 199
            ],
          ),
        ),
      ),
    );

    final Element child0 = tester.element(find.byKey(child0Key));
200
    final StackParentData child0RenderObjectParentData = child0.renderObject!.parentData! as StackParentData;
201
    expect(child0RenderObjectParentData.offset, equals(Offset.zero));
202 203

    final Element child1 = tester.element(find.byKey(child1Key));
204
    final StackParentData child1RenderObjectParentData = child1.renderObject!.parentData! as StackParentData;
205 206 207
    expect(child1RenderObjectParentData.offset, equals(const Offset(5.0, 5.0)));

    await tester.pumpWidget(
208
      const Directionality(
209
        textDirection: TextDirection.rtl,
210 211
        child: Center(
          child: Stack(
212
            alignment: AlignmentDirectional.bottomEnd,
213
            children: <Widget>[
214 215
              SizedBox(key: child0Key, width: 20.0, height: 20.0),
              SizedBox(key: child1Key, width: 10.0, height: 10.0),
216 217 218 219 220 221
            ],
          ),
        ),
      ),
    );

222
    expect(child0RenderObjectParentData.offset, equals(Offset.zero));
223
    expect(child1RenderObjectParentData.offset, equals(const Offset(0.0, 10.0)));
Hans Muller's avatar
Hans Muller committed
224 225
  });

226
  testWidgets('Can construct an empty IndexedStack', (WidgetTester tester) async {
227
    await tester.pumpWidget(
228
      const Directionality(
229
        textDirection: TextDirection.ltr,
230
        child: IndexedStack(),
231 232
      ),
    );
Hans Muller's avatar
Hans Muller committed
233 234
  });

235
  testWidgets('Can construct an empty Centered IndexedStack', (WidgetTester tester) async {
236
    await tester.pumpWidget(
237
      const Directionality(
238
        textDirection: TextDirection.ltr,
239
        child: Center(child: IndexedStack()),
240 241
      ),
    );
242 243
  });

244
  testWidgets('Can construct an IndexedStack', (WidgetTester tester) async {
245
    const int itemCount = 3;
246
    late List<int> itemsPainted;
247 248 249

    Widget buildFrame(int index) {
      itemsPainted = <int>[];
250 251 252
      final List<Widget> items = List<Widget>.generate(itemCount, (int i) {
        return CustomPaint(
          painter: TestCallbackPainter(
253
            onPaint: () { itemsPainted.add(i); },
254
          ),
255
          child: Text('$i', textDirection: TextDirection.ltr),
256 257
        );
      });
258 259
      return Center(
        child: IndexedStack(
260
          alignment: Alignment.topLeft,
261
          index: index,
262
          children: items,
263 264
        ),
      );
265 266
    }

267
    await tester.pumpWidget(buildFrame(0));
268 269 270
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsOneWidget);
    expect(find.text('2'), findsOneWidget);
271
    expect(itemsPainted, equals(<int>[0]));
272

273
    await tester.pumpWidget(buildFrame(1));
274
    expect(itemsPainted, equals(<int>[1]));
275

276
    await tester.pumpWidget(buildFrame(2));
277
    expect(itemsPainted, equals(<int>[2]));
Hans Muller's avatar
Hans Muller committed
278 279
  });

280
  testWidgets('Can hit test an IndexedStack', (WidgetTester tester) async {
281
    const Key key = Key('indexedStack');
282
    const int itemCount = 3;
283
    late List<int> itemsTapped;
284 285 286

    Widget buildFrame(int index) {
      itemsTapped = <int>[];
287 288
      final List<Widget> items = List<Widget>.generate(itemCount, (int i) {
        return GestureDetector(child: Text('$i', textDirection: TextDirection.ltr), onTap: () { itemsTapped.add(i); });
289
      });
290 291
      return Center(
        child: IndexedStack(
292
          alignment: Alignment.topLeft,
293 294
          key: key,
          index: index,
295
          children: items,
296 297
        ),
      );
298 299
    }

300
    await tester.pumpWidget(buildFrame(0));
301
    expect(itemsTapped, isEmpty);
302
    await tester.tap(find.byKey(key));
303
    expect(itemsTapped, <int>[0]);
304

305
    await tester.pumpWidget(buildFrame(2));
306
    expect(itemsTapped, isEmpty);
307
    await tester.tap(find.byKey(key));
308
    expect(itemsTapped, <int>[2]);
Hans Muller's avatar
Hans Muller committed
309 310
  });

311
  testWidgets('Can set width and height', (WidgetTester tester) async {
312
    const Key key = Key('container');
313

314 315
    const BoxDecoration kBoxDecoration = BoxDecoration(
      color: Color(0xFF00FF00),
316 317
    );

318
    await tester.pumpWidget(
319
      const Stack(
320
        textDirection: TextDirection.ltr,
321
        children: <Widget>[
322
          Positioned(
323 324 325
            left: 10.0,
            width: 11.0,
            height: 12.0,
326
            child: DecoratedBox(key: key, decoration: kBoxDecoration),
327 328 329
          ),
        ],
      ),
330 331 332 333 334 335 336
    );

    Element box;
    RenderBox renderBox;
    StackParentData parentData;

    box = tester.element(find.byKey(key));
337 338
    renderBox = box.renderObject! as RenderBox;
    parentData = renderBox.parentData! as StackParentData;
339 340 341 342 343 344 345 346 347 348 349
    expect(parentData.top, isNull);
    expect(parentData.right, isNull);
    expect(parentData.bottom, isNull);
    expect(parentData.left, equals(10.0));
    expect(parentData.width, equals(11.0));
    expect(parentData.height, equals(12.0));
    expect(parentData.offset.dx, equals(10.0));
    expect(parentData.offset.dy, equals(0.0));
    expect(renderBox.size.width, equals(11.0));
    expect(renderBox.size.height, equals(12.0));

350
    await tester.pumpWidget(
351
      const Stack(
352
        textDirection: TextDirection.ltr,
353
        children: <Widget>[
354
          Positioned(
355 356 357
            right: 10.0,
            width: 11.0,
            height: 12.0,
358
            child: DecoratedBox(key: key, decoration: kBoxDecoration),
359 360 361
          ),
        ],
      ),
362 363 364
    );

    box = tester.element(find.byKey(key));
365 366
    renderBox = box.renderObject! as RenderBox;
    parentData = renderBox.parentData! as StackParentData;
367 368 369 370 371 372 373 374 375 376
    expect(parentData.top, isNull);
    expect(parentData.right, equals(10.0));
    expect(parentData.bottom, isNull);
    expect(parentData.left, isNull);
    expect(parentData.width, equals(11.0));
    expect(parentData.height, equals(12.0));
    expect(parentData.offset.dx, equals(779.0));
    expect(parentData.offset.dy, equals(0.0));
    expect(renderBox.size.width, equals(11.0));
    expect(renderBox.size.height, equals(12.0));
377 378
  });

379
  testWidgets('Can set and update clipBehavior', (WidgetTester tester) async {
380
    await tester.pumpWidget(const Stack(textDirection: TextDirection.ltr));
381 382 383
    final RenderStack renderObject = tester.allRenderObjects.whereType<RenderStack>().first;
    expect(renderObject.clipBehavior, equals(Clip.hardEdge));

384
    await tester.pumpWidget(const Stack(textDirection: TextDirection.ltr));
385 386 387
    expect(renderObject.clipBehavior, equals(Clip.hardEdge));
  });

388
  testWidgets('Clip.none is respected by describeApproximateClip', (WidgetTester tester) async {
389
    await tester.pumpWidget(const Stack(
390
      textDirection: TextDirection.ltr,
391
      children: <Widget>[Positioned(left: 1000, right: 2000, child: SizedBox(width: 2000, height: 2000))],
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
    ));
    final RenderStack renderObject = tester.allRenderObjects.whereType<RenderStack>().first;
    expect(renderObject.clipBehavior, equals(Clip.hardEdge));

    bool visited = false;
    renderObject.visitChildren((RenderObject child) {
      visited = true;
      expect(renderObject.describeApproximatePaintClip(child), const Rect.fromLTRB(0.0, 0.0, 800.0, 600.0));
    });
    expect(visited, true);
    visited = false;
    renderObject.clipBehavior = Clip.none;
    renderObject.visitChildren((RenderObject child) {
      visited = true;
      expect(renderObject.describeApproximatePaintClip(child), null);
    });
    expect(visited, true);
  });

411
  testWidgets('IndexedStack with null index', (WidgetTester tester) async {
412
    bool? tapped;
413 414

    await tester.pumpWidget(
415
      Directionality(
416
        textDirection: TextDirection.ltr,
417 418
        child: Center(
          child: IndexedStack(
419 420
            index: null,
            children: <Widget>[
421
              GestureDetector(
422
                behavior: HitTestBehavior.opaque,
Ian Hickson's avatar
Ian Hickson committed
423
                onTap: () { tapped = true; },
424 425 426 427
                child: const SizedBox(
                  width: 200.0,
                  height: 200.0,
                ),
428
              ),
429 430
            ],
          ),
431 432 433 434
        ),
      ),
    );

435
    await tester.tap(find.byType(IndexedStack), warnIfMissed: false);
436
    final RenderBox box = tester.renderObject(find.byType(IndexedStack));
437 438 439 440
    expect(box.size, equals(const Size(200.0, 200.0)));
    expect(tapped, isNull);
  });

441 442
  testWidgets('Stack clip test', (WidgetTester tester) async {
    await tester.pumpWidget(
443
      const Directionality(
444
        textDirection: TextDirection.ltr,
445 446
        child: Center(
          child: Stack(
447
            children: <Widget>[
448
              SizedBox(
449 450 451
                width: 100.0,
                height: 100.0,
              ),
452
              Positioned(
453 454
                top: 0.0,
                left: 0.0,
455
                child: SizedBox(
456 457 458 459 460 461 462 463
                  width: 200.0,
                  height: 200.0,
                ),
              ),
            ],
          ),
        ),
      ),
464 465 466
    );

    RenderBox box = tester.renderObject(find.byType(Stack));
467
    TestPaintingContext context = TestPaintingContext();
468 469 470 471
    box.paint(context, Offset.zero);
    expect(context.invocations.first.memberName, equals(#pushClipRect));

    await tester.pumpWidget(
472
      const Directionality(
473
        textDirection: TextDirection.ltr,
474 475
        child: Center(
          child: Stack(
476
            clipBehavior: Clip.none,
477
            children: <Widget>[
478
              SizedBox(
479 480 481
                width: 100.0,
                height: 100.0,
              ),
482
              Positioned(
483 484
                top: 0.0,
                left: 0.0,
485
                child: SizedBox(
486 487 488 489 490 491 492 493
                  width: 200.0,
                  height: 200.0,
                ),
              ),
            ],
          ),
        ),
      ),
494 495 496
    );

    box = tester.renderObject(find.byType(Stack));
497
    context = TestPaintingContext();
498 499 500
    box.paint(context, Offset.zero);
    expect(context.invocations.first.memberName, equals(#paintChild));
  });
501 502 503 504

  testWidgets('Stack sizing: default', (WidgetTester tester) async {
    final List<String> logs = <String>[];
    await tester.pumpWidget(
505
      Directionality(
506
        textDirection: TextDirection.ltr,
507 508
        child: Center(
          child: ConstrainedBox(
509 510 511 512 513 514
            constraints: const BoxConstraints(
              minWidth: 2.0,
              maxWidth: 3.0,
              minHeight: 5.0,
              maxHeight: 7.0,
            ),
515
            child: Stack(
516
              children: <Widget>[
517
                LayoutBuilder(
518 519 520 521 522 523 524
                  builder: (BuildContext context, BoxConstraints constraints) {
                    logs.add(constraints.toString());
                    return const Placeholder();
                  },
                ),
              ],
            ),
525 526 527 528 529 530 531 532 533 534
          ),
        ),
      ),
    );
    expect(logs, <String>['BoxConstraints(0.0<=w<=3.0, 0.0<=h<=7.0)']);
  });

  testWidgets('Stack sizing: explicit', (WidgetTester tester) async {
    final List<String> logs = <String>[];
    Widget buildStack(StackFit sizing) {
535
      return Directionality(
536
        textDirection: TextDirection.ltr,
537 538
        child: Center(
          child: ConstrainedBox(
539 540 541 542 543 544
            constraints: const BoxConstraints(
              minWidth: 2.0,
              maxWidth: 3.0,
              minHeight: 5.0,
              maxHeight: 7.0,
            ),
545
            child: Stack(
546 547
              fit: sizing,
              children: <Widget>[
548
                LayoutBuilder(
549 550 551 552 553 554 555
                  builder: (BuildContext context, BoxConstraints constraints) {
                    logs.add(constraints.toString());
                    return const Placeholder();
                  },
                ),
              ],
            ),
556 557 558 559 560 561 562 563 564 565 566 567 568 569
          ),
        ),
      );
    }
    await tester.pumpWidget(buildStack(StackFit.loose));
    logs.add('=1=');
    await tester.pumpWidget(buildStack(StackFit.expand));
    logs.add('=2=');
    await tester.pumpWidget(buildStack(StackFit.passthrough));
    expect(logs, <String>[
      'BoxConstraints(0.0<=w<=3.0, 0.0<=h<=7.0)',
      '=1=',
      'BoxConstraints(w=3.0, h=7.0)',
      '=2=',
570
      'BoxConstraints(2.0<=w<=3.0, 5.0<=h<=7.0)',
571 572
    ]);
  });
573 574

  testWidgets('Positioned.directional control test', (WidgetTester tester) async {
575
    final Key key = UniqueKey();
576
    await tester.pumpWidget(
577
      Directionality(
578
        textDirection: TextDirection.ltr,
579
        child: Stack(
580
          children: <Widget>[
581
            Positioned.directional(
582 583
              textDirection: TextDirection.rtl,
              start: 50.0,
584
              child: SizedBox(key: key, width: 75.0, height: 175.0),
585 586
            ),
          ],
587
        ),
588 589
      ),
    );
590 591 592

    expect(tester.getTopLeft(find.byKey(key)), const Offset(675.0, 0.0));

593
    await tester.pumpWidget(
594
      Directionality(
595
        textDirection: TextDirection.ltr,
596
        child: Stack(
597
          children: <Widget>[
598
            Positioned.directional(
599 600
              textDirection: TextDirection.ltr,
              start: 50.0,
601
              child: SizedBox(key: key, width: 75.0, height: 175.0),
602 603
            ),
          ],
604
        ),
605 606
      ),
    );
607 608 609 610 611

    expect(tester.getTopLeft(find.byKey(key)), const Offset(50.0, 0.0));
  });

  testWidgets('PositionedDirectional control test', (WidgetTester tester) async {
612
    final Key key = UniqueKey();
613
    await tester.pumpWidget(
614
      Directionality(
615
        textDirection: TextDirection.rtl,
616
        child: Stack(
617
          children: <Widget>[
618
            PositionedDirectional(
619
              start: 50.0,
620
              child: SizedBox(key: key, width: 75.0, height: 175.0),
621 622 623
            ),
          ],
        ),
624
      ),
625 626 627 628 629
    );

    expect(tester.getTopLeft(find.byKey(key)), const Offset(675.0, 0.0));

    await tester.pumpWidget(
630
      Directionality(
631
        textDirection: TextDirection.ltr,
632
        child: Stack(
633
          children: <Widget>[
634
            PositionedDirectional(
635
              start: 50.0,
636
              child: SizedBox(key: key, width: 75.0, height: 175.0),
637 638 639
            ),
          ],
        ),
640
      ),
641 642 643 644
    );

    expect(tester.getTopLeft(find.byKey(key)), const Offset(50.0, 0.0));
  });
Ian Hickson's avatar
Ian Hickson committed
645 646 647

  testWidgets('Can change the text direction of a Stack', (WidgetTester tester) async {
    await tester.pumpWidget(
648
      const Stack(
649
        alignment: Alignment.center,
Ian Hickson's avatar
Ian Hickson committed
650 651 652
      ),
    );
    await tester.pumpWidget(
653
      const Stack(
Ian Hickson's avatar
Ian Hickson committed
654 655 656 657
        textDirection: TextDirection.rtl,
      ),
    );
    await tester.pumpWidget(
658
      const Stack(
659
        alignment: Alignment.center,
Ian Hickson's avatar
Ian Hickson committed
660 661 662
      ),
    );
  });
663 664 665

  testWidgets('Alignment with partially-positioned children', (WidgetTester tester) async {
    await tester.pumpWidget(
666
      const Directionality(
667
        textDirection: TextDirection.rtl,
668
        child: Stack(
669
          alignment: Alignment.center,
670
          children: <Widget>[
671 672 673 674 675 676 677 678 679
            SizedBox(width: 100.0, height: 100.0),
            Positioned(left: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(right: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(start: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(end: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
680 681 682 683
          ],
        ),
      ),
    );
Dan Field's avatar
Dan Field committed
684 685 686 687 688 689 690 691 692
    expect(tester.getRect(find.byType(SizedBox).at(0)), const Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(1)), const Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(2)), const Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(3)), const Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(4)), const Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(5)), const Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(6)), const Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(7)), const Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(8)), const Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
693 694

    await tester.pumpWidget(
695
      const Directionality(
696
        textDirection: TextDirection.ltr,
697
        child: Stack(
698
          alignment: Alignment.center,
699
          children: <Widget>[
700 701 702 703 704 705 706 707 708
            SizedBox(width: 100.0, height: 100.0),
            Positioned(left: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(right: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(start: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(end: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
709 710 711 712
          ],
        ),
      ),
    );
Dan Field's avatar
Dan Field committed
713 714 715 716 717 718 719 720 721
    expect(tester.getRect(find.byType(SizedBox).at(0)), const Rect.fromLTWH(350.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(1)), const Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(2)), const Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(3)), const Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(4)), const Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(5)), const Rect.fromLTWH(0.0,   250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(6)), const Rect.fromLTWH(700.0, 250.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(7)), const Rect.fromLTWH(350.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(8)), const Rect.fromLTWH(350.0, 500.0, 100.0, 100.0));
722 723

    await tester.pumpWidget(
724
      const Directionality(
725
        textDirection: TextDirection.ltr,
726
        child: Stack(
727
          alignment: Alignment.bottomRight,
728
          children: <Widget>[
729 730 731 732 733 734 735 736 737
            SizedBox(width: 100.0, height: 100.0),
            Positioned(left: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(right: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(start: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(end: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
738 739 740 741
          ],
        ),
      ),
    );
Dan Field's avatar
Dan Field committed
742 743 744 745 746 747 748 749 750
    expect(tester.getRect(find.byType(SizedBox).at(0)), const Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(1)), const Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(2)), const Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(3)), const Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(4)), const Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(5)), const Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(6)), const Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(7)), const Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(8)), const Rect.fromLTWH(700.0, 500.0, 100.0, 100.0));
751 752

    await tester.pumpWidget(
753
      const Directionality(
754
        textDirection: TextDirection.ltr,
755
        child: Stack(
756
          alignment: Alignment.topLeft,
757
          children: <Widget>[
758 759 760 761 762 763 764 765 766
            SizedBox(width: 100.0, height: 100.0),
            Positioned(left: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(right: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            Positioned(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(start: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(end: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(top: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
            PositionedDirectional(bottom: 0.0, child: SizedBox(width: 100.0, height: 100.0)),
767 768 769 770
          ],
        ),
      ),
    );
Dan Field's avatar
Dan Field committed
771 772 773 774 775 776 777 778 779
    expect(tester.getRect(find.byType(SizedBox).at(0)), const Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(1)), const Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(2)), const Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(3)), const Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(4)), const Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(5)), const Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(6)), const Rect.fromLTWH(700.0, 0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(7)), const Rect.fromLTWH(0.0,   0.0,   100.0, 100.0));
    expect(tester.getRect(find.byType(SizedBox).at(8)), const Rect.fromLTWH(0.0,   500.0, 100.0, 100.0));
780
  });
781 782 783

  testWidgets('Stack error messages', (WidgetTester tester) async {
    await tester.pumpWidget(
784
      const Stack(),
785
    );
786 787
    final String exception = tester.takeException().toString();

788
    expect(
789
      exception, startsWith(
790
      'No Directionality widget found.\n'
791 792
      "Stack widgets require a Directionality widget ancestor to resolve the 'alignment' argument.\n"
      "The default value for 'alignment' is AlignmentDirectional.topStart, which requires a text direction.\n"
793 794
      'The specific widget that could not find a Directionality ancestor was:\n'
      '  Stack\n'
795 796 797 798 799
      'The ownership chain for the affected widget is: "Stack ← ', // Omitted full ownership chain because it is not relevant for the test.
    ));
    expect(
      exception, endsWith(
      '← [root]"\n' // End of ownership chain.
800 801 802 803 804
      'Typically, the Directionality widget is introduced by the MaterialApp or WidgetsApp widget at the '
      'top of your application widget tree. It determines the ambient reading direction and is used, for '
      'example, to determine how to lay out text, how to interpret "start" and "end" values, and to resolve '
      'EdgeInsetsDirectional, AlignmentDirectional, and other *Directional objects.\n'
      'Instead of providing a Directionality widget, another solution would be passing a non-directional '
805
      "'alignment', or an explicit 'textDirection', to the Stack.",
806
    ));
807
  });
808 809 810

  testWidgets('Can update clipBehavior of IndexedStack',
      (WidgetTester tester) async {
811
    await tester.pumpWidget(const IndexedStack(textDirection: TextDirection.ltr));
812 813 814 815 816 817
    final RenderIndexedStack renderObject =
      tester.renderObject<RenderIndexedStack>(find.byType(IndexedStack));
    expect(renderObject.clipBehavior, equals(Clip.hardEdge));

    // Update clipBehavior to Clip.antiAlias

818
    await tester.pumpWidget(const IndexedStack(
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
      textDirection: TextDirection.ltr,
      clipBehavior: Clip.antiAlias,
    ));
    final RenderIndexedStack renderIndexedObject =
      tester.renderObject<RenderIndexedStack>(find.byType(IndexedStack));
    expect(renderIndexedObject.clipBehavior, equals(Clip.antiAlias));
  });

  testWidgets('IndexedStack sizing: explicit', (WidgetTester tester) async {
    final List<String> logs = <String>[];
    Widget buildIndexedStack(StackFit sizing) {
      return Directionality(
        textDirection: TextDirection.ltr,
        child: Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(
              minWidth: 2.0,
              maxWidth: 3.0,
              minHeight: 5.0,
              maxHeight: 7.0,
            ),
            child: IndexedStack(
              sizing: sizing,
              children: <Widget>[
                LayoutBuilder(
                  builder: (BuildContext context, BoxConstraints constraints) {
                    logs.add(constraints.toString());
                    return const Placeholder();
                  },
                ),
              ],
            ),
          ),
        ),
      );
    }
    await tester.pumpWidget(buildIndexedStack(StackFit.loose));
    logs.add('=1=');
    await tester.pumpWidget(buildIndexedStack(StackFit.expand));
    logs.add('=2=');
    await tester.pumpWidget(buildIndexedStack(StackFit.passthrough));
    expect(logs, <String>[
      'BoxConstraints(0.0<=w<=3.0, 0.0<=h<=7.0)',
      '=1=',
      'BoxConstraints(w=3.0, h=7.0)',
      '=2=',
      'BoxConstraints(2.0<=w<=3.0, 5.0<=h<=7.0)',
    ]);
  });
868
}