table_test.dart 33.7 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/material.dart';
6 7
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
Hixie's avatar
Hixie committed
8

9
class TestStatefulWidget extends StatefulWidget {
10
  const TestStatefulWidget({ super.key });
11 12

  @override
13
  TestStatefulWidgetState createState() => TestStatefulWidgetState();
14 15 16 17
}

class TestStatefulWidgetState extends State<TestStatefulWidget> {
  @override
18
  Widget build(BuildContext context) => Container();
19 20
}

21
class TestChildWidget extends StatefulWidget {
22
  const TestChildWidget({ super.key });
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

  @override
  TestChildState createState() => TestChildState();
}

class TestChildState extends State<TestChildWidget> {
  bool toggle = true;

  void toggleMe() {
    setState(() { toggle = !toggle; });
  }

  @override
  Widget build(BuildContext context) => toggle ? const SizedBox() : const Text('CRASHHH');
}

Hixie's avatar
Hixie committed
39
void main() {
40 41
  testWidgets('Table widget - empty', (WidgetTester tester) async {
    await tester.pumpWidget(
42
      Directionality(
43
        textDirection: TextDirection.ltr,
44
        child: Table(),
45 46 47
      ),
    );
  });
48

49
  testWidgets('Table widget - control test', (WidgetTester tester) async {
50
    Future<void> run(TextDirection textDirection) async {
51
      await tester.pumpWidget(
52
        Directionality(
53
          textDirection: textDirection,
54
          child: Table(
55
            children: const <TableRow>[
56 57 58
              TableRow(
                children: <Widget>[
                  Text('AAAAAA'), Text('B'), Text('C'),
59 60
                ],
              ),
61 62 63
              TableRow(
                children: <Widget>[
                  Text('D'), Text('EEE'), Text('F'),
64 65
                ],
              ),
66 67 68
              TableRow(
                children: <Widget>[
                  Text('G'), Text('H'), Text('III'),
69 70 71
                ],
              ),
            ],
72
          ),
73 74 75 76 77 78 79 80 81 82 83 84
        ),
      );
      final RenderBox boxA = tester.renderObject(find.text('AAAAAA'));
      final RenderBox boxD = tester.renderObject(find.text('D'));
      final RenderBox boxG = tester.renderObject(find.text('G'));
      final RenderBox boxB = tester.renderObject(find.text('B'));
      expect(boxA.size, equals(boxD.size));
      expect(boxA.size, equals(boxG.size));
      expect(boxA.size, equals(boxB.size));
    }

    await run(TextDirection.ltr);
85
    await tester.pumpWidget(Container());
86
    await run(TextDirection.rtl);
Hixie's avatar
Hixie committed
87
  });
88

89 90 91 92 93 94
  testWidgets('Table widget can be detached and re-attached', (WidgetTester tester) async {
    final Widget table = Table(
      key: GlobalKey(),
      children: const <TableRow>[
        TableRow(
          decoration: BoxDecoration(
95
              color: Colors.yellow,
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
          ),
          children: <Widget>[Placeholder()],
        ),
      ],
    );
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Center(
          child: table,
        ),
      ),
    );
    // Move table to a different location to simulate detaching and re-attaching effect.
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Center(
          child: Center(
115
            child: table,
116 117 118 119 120 121 122 123
          ),
        ),
      ),
    );

    expect(tester.takeException(), isNull);
  });

124 125
  testWidgets('Table widget - column offset (LTR)', (WidgetTester tester) async {
    await tester.pumpWidget(
126
      Directionality(
127
        textDirection: TextDirection.ltr,
128 129
        child: Center(
          child: Table(
130
            columnWidths: const <int, TableColumnWidth>{
131 132 133
              0: FixedColumnWidth(100.0),
              1: FixedColumnWidth(110.0),
              2: FixedColumnWidth(125.0),
134 135
            },
            defaultColumnWidth: const FixedColumnWidth(333.0),
136
            children: const <TableRow>[
137 138 139
              TableRow(
                children: <Widget>[
                  Text('A1'), Text('B1'), Text('C1'),
140 141
                ],
              ),
142 143 144
              TableRow(
                children: <Widget>[
                  Text('A2'), Text('B2'), Text('C2'),
145 146
                ],
              ),
147 148 149
              TableRow(
                children: <Widget>[
                  Text('A3'), Text('B3'), Text('C3'),
150 151 152
                ],
              ),
            ],
153
          ),
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
        ),
      ),
    );

    final Rect table = tester.getRect(find.byType(Table));
    final Rect a1 = tester.getRect(find.text('A1'));
    final Rect a2 = tester.getRect(find.text('A2'));
    final Rect a3 = tester.getRect(find.text('A3'));
    final Rect b1 = tester.getRect(find.text('B1'));
    final Rect b2 = tester.getRect(find.text('B2'));
    final Rect b3 = tester.getRect(find.text('B3'));
    final Rect c1 = tester.getRect(find.text('C1'));
    final Rect c2 = tester.getRect(find.text('C2'));
    final Rect c3 = tester.getRect(find.text('C3'));

    expect(a1.width, equals(100.0));
    expect(a2.width, equals(100.0));
    expect(a3.width, equals(100.0));
    expect(b1.width, equals(110.0));
    expect(b2.width, equals(110.0));
    expect(b3.width, equals(110.0));
    expect(c1.width, equals(125.0));
    expect(c2.width, equals(125.0));
    expect(c3.width, equals(125.0));

    expect(table.width, equals(335.0));

    expect(a1.left, equals(table.left));
    expect(a2.left, equals(a1.left));
    expect(a3.left, equals(a1.left));

    expect(b1.left, equals(table.left + a1.width));
    expect(b2.left, equals(b1.left));
    expect(b3.left, equals(b1.left));

    expect(c1.left, equals(table.left + a1.width + b1.width));
    expect(c2.left, equals(c1.left));
    expect(c3.left, equals(c1.left));
  });

  testWidgets('Table widget - column offset (RTL)', (WidgetTester tester) async {
    await tester.pumpWidget(
196
      Directionality(
197
        textDirection: TextDirection.rtl,
198 199
        child: Center(
          child: Table(
200
            columnWidths: const <int, TableColumnWidth>{
201 202 203
              0: FixedColumnWidth(100.0),
              1: FixedColumnWidth(110.0),
              2: FixedColumnWidth(125.0),
204 205
            },
            defaultColumnWidth: const FixedColumnWidth(333.0),
206
            children: const <TableRow>[
207 208 209
              TableRow(
                children: <Widget>[
                  Text('A1'), Text('B1'), Text('C1'),
210 211
                ],
              ),
212 213 214
              TableRow(
                children: <Widget>[
                  Text('A2'), Text('B2'), Text('C2'),
215 216
                ],
              ),
217 218 219
              TableRow(
                children: <Widget>[
                  Text('A3'), Text('B3'), Text('C3'),
220 221 222
                ],
              ),
            ],
223
          ),
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
        ),
      ),
    );

    final Rect table = tester.getRect(find.byType(Table));
    final Rect a1 = tester.getRect(find.text('A1'));
    final Rect a2 = tester.getRect(find.text('A2'));
    final Rect a3 = tester.getRect(find.text('A3'));
    final Rect b1 = tester.getRect(find.text('B1'));
    final Rect b2 = tester.getRect(find.text('B2'));
    final Rect b3 = tester.getRect(find.text('B3'));
    final Rect c1 = tester.getRect(find.text('C1'));
    final Rect c2 = tester.getRect(find.text('C2'));
    final Rect c3 = tester.getRect(find.text('C3'));

    expect(a1.width, equals(100.0));
    expect(a2.width, equals(100.0));
    expect(a3.width, equals(100.0));
    expect(b1.width, equals(110.0));
    expect(b2.width, equals(110.0));
    expect(b3.width, equals(110.0));
    expect(c1.width, equals(125.0));
    expect(c2.width, equals(125.0));
    expect(c3.width, equals(125.0));

    expect(table.width, equals(335.0));

    expect(a1.right, equals(table.right));
    expect(a2.right, equals(a1.right));
    expect(a3.right, equals(a1.right));

    expect(b1.right, equals(table.right - a1.width));
    expect(b2.right, equals(b1.right));
    expect(b3.right, equals(b1.right));

    expect(c1.right, equals(table.right - a1.width - b1.width));
    expect(c2.right, equals(c1.right));
    expect(c3.right, equals(c1.right));
  });

  testWidgets('Table border - smoke test', (WidgetTester tester) async {
265
    Future<void> run(TextDirection textDirection) async {
266
      await tester.pumpWidget(
267
        Directionality(
268
          textDirection: textDirection,
269 270
          child: Table(
            border: TableBorder.all(),
271
            children: const <TableRow>[
272 273 274
              TableRow(
                children: <Widget>[
                  Text('AAAAAA'), Text('B'), Text('C'),
275 276
                ],
              ),
277 278 279
              TableRow(
                children: <Widget>[
                  Text('D'), Text('EEE'), Text('F'),
280 281
                ],
              ),
282 283 284
              TableRow(
                children: <Widget>[
                  Text('G'), Text('H'), Text('III'),
285 286 287
                ],
              ),
            ],
288
          ),
289 290 291 292 293
        ),
      );
    }

    await run(TextDirection.ltr);
294
    await tester.pumpWidget(Container());
295 296 297 298 299
    await run(TextDirection.rtl);
  });

  testWidgets('Table widget - changing table dimensions', (WidgetTester tester) async {
    await tester.pumpWidget(
300
      Directionality(
301
        textDirection: TextDirection.ltr,
302
        child: Table(
303
          children: const <TableRow>[
304 305 306
            TableRow(
              children: <Widget>[
                Text('A'), Text('B'), Text('C'),
307 308
              ],
            ),
309 310 311
            TableRow(
              children: <Widget>[
                Text('D'), Text('E'), Text('F'),
312 313
              ],
            ),
314 315 316
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('I'),
317 318 319 320 321 322
              ],
            ),
          ],
        ),
      ),
    );
323 324
    final RenderBox boxA1 = tester.renderObject(find.text('A'));
    final RenderBox boxG1 = tester.renderObject(find.text('G'));
325 326
    expect(boxA1, isNotNull);
    expect(boxG1, isNotNull);
327
    await tester.pumpWidget(
328
      Directionality(
329
        textDirection: TextDirection.ltr,
330
        child: Table(
331
          children: const <TableRow>[
332 333 334
            TableRow(
              children: <Widget>[
                Text('a'), Text('b'), Text('c'), Text('d'),
335 336
              ],
            ),
337 338 339
            TableRow(
              children: <Widget>[
                Text('e'), Text('f'), Text('g'), Text('h'),
340 341 342 343 344 345
              ],
            ),
          ],
        ),
      ),
    );
346 347
    final RenderBox boxA2 = tester.renderObject(find.text('a'));
    final RenderBox boxG2 = tester.renderObject(find.text('g'));
348 349 350 351
    expect(boxA2, isNotNull);
    expect(boxG2, isNotNull);
    expect(boxA1, equals(boxA2));
    expect(boxG1, isNot(equals(boxG2)));
Hixie's avatar
Hixie committed
352
  });
353

354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
  testWidgets('Really small deficit double precision error', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/27083
    const SizedBox cell = SizedBox(width: 16, height: 16);
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Table(
          children: const <TableRow>[
            TableRow(
              children: <Widget>[
                cell, cell, cell, cell, cell, cell,
              ],
            ),
            TableRow(
              children: <Widget>[
                cell, cell, cell, cell, cell, cell,
              ],
            ),
          ],
        ),
      ),
    );
    // If the above bug is present this test will never terminate.
  });

379 380 381 382 383 384 385 386 387 388 389
  testWidgets('Calculating flex columns with small width deficit', (WidgetTester tester) async {
    const SizedBox cell = SizedBox(width: 1, height: 1);
    // If the error is present, pumpWidget() will fail due to an unsatisfied
    // assertion during the layout phase.
    await tester.pumpWidget(
      ConstrainedBox(
        constraints: BoxConstraints.tight(const Size(600, 800)),
        child: Directionality(
          textDirection: TextDirection.ltr,
          child: Table(
            columnWidths: const <int, TableColumnWidth>{
390
              0: FlexColumnWidth(),
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
              1: FlexColumnWidth(0.123),
              2: FlexColumnWidth(0.123),
              3: FlexColumnWidth(0.123),
              4: FlexColumnWidth(0.123),
              5: FlexColumnWidth(0.123),
              6: FlexColumnWidth(0.123),
            },
            children: <TableRow>[
              TableRow(children: List<Widget>.filled(7, cell)),
              TableRow(children: List<Widget>.filled(7, cell)),
            ],
          ),
        ),
      ),
    );
    expect(tester.takeException(), null);
  });

409
  testWidgets('Table widget - repump test', (WidgetTester tester) async {
410
    await tester.pumpWidget(
411
      Directionality(
412
        textDirection: TextDirection.ltr,
413
        child: Table(
414
          children: const <TableRow>[
415 416 417
            TableRow(
              children: <Widget>[
                Text('AAAAAA'), Text('B'), Text('C'),
418 419
              ],
            ),
420 421 422
            TableRow(
              children: <Widget>[
                Text('D'), Text('EEE'), Text('F'),
423 424
              ],
            ),
425 426 427
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
428 429 430 431 432 433 434
              ],
            ),
          ],
        ),
      ),
    );
    await tester.pumpWidget(
435
      Directionality(
436
        textDirection: TextDirection.ltr,
437
        child: Table(
438
          children: const <TableRow>[
439 440 441
            TableRow(
              children: <Widget>[
                Text('AAA'), Text('B'), Text('C'),
442 443
              ],
            ),
444 445 446
            TableRow(
              children: <Widget>[
                Text('D'), Text('E'), Text('FFFFFF'),
447 448
              ],
            ),
449 450 451
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
452 453 454 455 456 457
              ],
            ),
          ],
        ),
      ),
    );
458 459 460 461
    final RenderBox boxA = tester.renderObject(find.text('AAA'));
    final RenderBox boxD = tester.renderObject(find.text('D'));
    final RenderBox boxG = tester.renderObject(find.text('G'));
    final RenderBox boxB = tester.renderObject(find.text('B'));
462 463 464
    expect(boxA.size, equals(boxD.size));
    expect(boxA.size, equals(boxG.size));
    expect(boxA.size, equals(boxB.size));
Hixie's avatar
Hixie committed
465
  });
466

467
  testWidgets('Table widget - intrinsic sizing test', (WidgetTester tester) async {
468
    await tester.pumpWidget(
469
      Directionality(
Ian Hickson's avatar
Ian Hickson committed
470
      textDirection: TextDirection.ltr,
471
        child: Table(
472
          defaultColumnWidth: const IntrinsicColumnWidth(),
473
          children: const <TableRow>[
474 475 476
            TableRow(
              children: <Widget>[
                Text('AAA'), Text('B'), Text('C'),
477 478
              ],
            ),
479 480 481
            TableRow(
              children: <Widget>[
                Text('D'), Text('E'), Text('FFFFFF'),
482 483
              ],
            ),
484 485 486
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
487 488 489 490 491 492
              ],
            ),
          ],
        ),
      ),
    );
493 494 495 496
    final RenderBox boxA = tester.renderObject(find.text('AAA'));
    final RenderBox boxD = tester.renderObject(find.text('D'));
    final RenderBox boxG = tester.renderObject(find.text('G'));
    final RenderBox boxB = tester.renderObject(find.text('B'));
497 498 499 500
    expect(boxA.size, equals(boxD.size));
    expect(boxA.size, equals(boxG.size));
    expect(boxA.size.width, greaterThan(boxB.size.width));
    expect(boxA.size.height, equals(boxB.size.height));
Hixie's avatar
Hixie committed
501
  });
502

503
  testWidgets('Table widget - intrinsic sizing test, resizing', (WidgetTester tester) async {
504
    await tester.pumpWidget(
505
      Directionality(
506
        textDirection: TextDirection.ltr,
507
        child: Table(
508
          defaultColumnWidth: const IntrinsicColumnWidth(),
509
          children: const <TableRow>[
510 511 512
            TableRow(
              children: <Widget>[
                Text('AAAAAA'), Text('B'), Text('C'),
513 514
              ],
            ),
515 516 517
            TableRow(
              children: <Widget>[
                Text('D'), Text('EEE'), Text('F'),
518 519
              ],
            ),
520 521 522
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
523 524 525 526 527 528 529
              ],
            ),
          ],
        ),
      ),
    );
    await tester.pumpWidget(
530
      Directionality(
531
        textDirection: TextDirection.ltr,
532
        child: Table(
533
          defaultColumnWidth: const IntrinsicColumnWidth(),
534
          children: const <TableRow>[
535 536 537
            TableRow(
              children: <Widget>[
                Text('A'), Text('B'), Text('C'),
538 539
              ],
            ),
540 541 542
            TableRow(
              children: <Widget>[
                Text('D'), Text('EEE'), Text('F'),
543 544
              ],
            ),
545 546 547
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
548 549 550 551 552 553
              ],
            ),
          ],
        ),
      ),
    );
554 555 556 557
    final RenderBox boxA = tester.renderObject(find.text('A'));
    final RenderBox boxD = tester.renderObject(find.text('D'));
    final RenderBox boxG = tester.renderObject(find.text('G'));
    final RenderBox boxB = tester.renderObject(find.text('B'));
558 559 560 561
    expect(boxA.size, equals(boxD.size));
    expect(boxA.size, equals(boxG.size));
    expect(boxA.size.width, lessThan(boxB.size.width));
    expect(boxA.size.height, equals(boxB.size.height));
Hixie's avatar
Hixie committed
562
  });
563

564
  testWidgets('Table widget - intrinsic sizing test, changing column widths', (WidgetTester tester) async {
565
    await tester.pumpWidget(
566
      Directionality(
567
        textDirection: TextDirection.ltr,
568
        child: Table(
569
          children: const <TableRow>[
570 571 572
            TableRow(
              children: <Widget>[
                Text('AAA'), Text('B'), Text('C'),
573 574
              ],
            ),
575 576 577
            TableRow(
              children: <Widget>[
                Text('D'), Text('E'), Text('FFFFFF'),
578 579
              ],
            ),
580 581 582
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
583 584 585 586 587 588 589
              ],
            ),
          ],
        ),
      ),
    );
    await tester.pumpWidget(
590
      Directionality(
591
        textDirection: TextDirection.ltr,
592
        child: Table(
593
          defaultColumnWidth: const IntrinsicColumnWidth(),
594
          children: const <TableRow>[
595 596 597
            TableRow(
              children: <Widget>[
                Text('AAA'), Text('B'), Text('C'),
598 599
              ],
            ),
600 601 602
            TableRow(
              children: <Widget>[
                Text('D'), Text('E'), Text('FFFFFF'),
603 604
              ],
            ),
605 606 607
            TableRow(
              children: <Widget>[
                Text('G'), Text('H'), Text('III'),
608 609 610
              ],
            ),
          ],
611
        ),
612 613
      ),
    );
614 615 616 617
    final RenderBox boxA = tester.renderObject(find.text('AAA'));
    final RenderBox boxD = tester.renderObject(find.text('D'));
    final RenderBox boxG = tester.renderObject(find.text('G'));
    final RenderBox boxB = tester.renderObject(find.text('B'));
618 619 620 621
    expect(boxA.size, equals(boxD.size));
    expect(boxA.size, equals(boxG.size));
    expect(boxA.size.width, greaterThan(boxB.size.width));
    expect(boxA.size.height, equals(boxB.size.height));
Hixie's avatar
Hixie committed
622
  });
623

624
  testWidgets('Table widget - moving test', (WidgetTester tester) async {
625
    final List<BuildContext> contexts = <BuildContext>[];
626
    await tester.pumpWidget(
627
      Directionality(
628
        textDirection: TextDirection.ltr,
629
        child: Table(
630
          children: <TableRow>[
631
            TableRow(
632 633
              key: const ValueKey<int>(1),
              children: <Widget>[
634
                StatefulBuilder(
635 636 637 638 639 640 641
                  builder: (BuildContext context, StateSetter setState) {
                    contexts.add(context);
                    return const Text('A');
                  },
                ),
              ],
            ),
642
            const TableRow(
643 644
              children: <Widget>[
                Text('b'),
645 646 647 648 649 650 651
              ],
            ),
          ],
        ),
      ),
    );
    await tester.pumpWidget(
652
      Directionality(
653
        textDirection: TextDirection.ltr,
654
        child: Table(
655
          children: <TableRow>[
656
            const TableRow(
657 658
              children: <Widget>[
                Text('b'),
659 660
              ],
            ),
661
            TableRow(
662 663
              key: const ValueKey<int>(1),
              children: <Widget>[
664
                StatefulBuilder(
665 666 667 668 669 670 671 672 673 674 675
                  builder: (BuildContext context, StateSetter setState) {
                    contexts.add(context);
                    return const Text('A');
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
676 677
    expect(contexts.length, equals(2));
    expect(contexts[0], equals(contexts[1]));
Hixie's avatar
Hixie committed
678
  });
679 680

  testWidgets('Table widget - keyed rows', (WidgetTester tester) async {
681
    await tester.pumpWidget(
682
      Directionality(
683
        textDirection: TextDirection.ltr,
684
        child: Table(
685
          children: const <TableRow>[
686 687 688 689 690
            TableRow(
              key: ValueKey<int>(1),
              children: <Widget>[
                TestStatefulWidget(key: ValueKey<int>(11)),
                TestStatefulWidget(key: ValueKey<int>(12)),
691 692
              ],
            ),
693 694 695 696 697
            TableRow(
              key: ValueKey<int>(2),
              children: <Widget>[
                TestStatefulWidget(key: ValueKey<int>(21)),
                TestStatefulWidget(key: ValueKey<int>(22)),
698 699 700 701 702 703
              ],
            ),
          ],
        ),
      ),
    );
704

705 706 707 708
    final TestStatefulWidgetState state11 = tester.state(find.byKey(const ValueKey<int>(11)));
    final TestStatefulWidgetState state12 = tester.state(find.byKey(const ValueKey<int>(12)));
    final TestStatefulWidgetState state21 = tester.state(find.byKey(const ValueKey<int>(21)));
    final TestStatefulWidgetState state22 = tester.state(find.byKey(const ValueKey<int>(22)));
709 710 711 712 713 714

    expect(state11.mounted, isTrue);
    expect(state12.mounted, isTrue);
    expect(state21.mounted, isTrue);
    expect(state22.mounted, isTrue);

715
    await tester.pumpWidget(
716
      Directionality(
717
        textDirection: TextDirection.ltr,
718
        child: Table(
719
          children: const <TableRow>[
720 721 722 723 724
            TableRow(
              key: ValueKey<int>(2),
              children: <Widget>[
                TestStatefulWidget(key: ValueKey<int>(21)),
                TestStatefulWidget(key: ValueKey<int>(22)),
725 726 727 728 729 730
              ],
            ),
          ],
        ),
      ),
    );
731 732 733 734 735 736 737 738

    expect(state11.mounted, isFalse);
    expect(state12.mounted, isFalse);
    expect(state21.mounted, isTrue);
    expect(state22.mounted, isTrue);
  });

  testWidgets('Table widget - global key reparenting', (WidgetTester tester) async {
739 740
    final GlobalKey key = GlobalKey();
    final Key tableKey = UniqueKey();
741 742

    await tester.pumpWidget(
743
      Directionality(
744
        textDirection: TextDirection.ltr,
745
        child: Column(
746
          children: <Widget> [
747
            Expanded(
748
              key: tableKey,
749
              child: Table(
750
                children: <TableRow>[
751
                  TableRow(
752
                    children: <Widget>[
753 754 755
                      Container(key: const ValueKey<int>(1)),
                      TestStatefulWidget(key: key),
                      Container(key: const ValueKey<int>(2)),
756 757 758 759
                    ],
                  ),
                ],
              ),
760
            ),
761 762
          ],
        ),
763 764 765
      ),
    );

766
    final RenderTable table = tester.renderObject(find.byType(Table));
767 768 769
    expect(table.row(0).length, 3);

    await tester.pumpWidget(
770
      Directionality(
771
        textDirection: TextDirection.ltr,
772
        child: Column(
773
          children: <Widget> [
774 775
            Expanded(child: TestStatefulWidget(key: key)),
            Expanded(
776
              key: tableKey,
777
              child: Table(
778
                children: <TableRow>[
779
                  TableRow(
780
                    children: <Widget>[
781 782
                      Container(key: const ValueKey<int>(1)),
                      Container(key: const ValueKey<int>(2)),
783 784 785 786
                    ],
                  ),
                ],
              ),
787
            ),
788 789
          ],
        ),
790 791 792 793 794 795 796
      ),
    );

    expect(tester.renderObject(find.byType(Table)), equals(table));
    expect(table.row(0).length, 2);

    await tester.pumpWidget(
797
      Directionality(
798
        textDirection: TextDirection.ltr,
799
        child: Column(
800
          children: <Widget> [
801
            Expanded(
802
              key: tableKey,
803
              child: Table(
804
                children: <TableRow>[
805
                  TableRow(
806
                    children: <Widget>[
807 808 809
                      Container(key: const ValueKey<int>(1)),
                      TestStatefulWidget(key: key),
                      Container(key: const ValueKey<int>(2)),
810 811 812 813
                    ],
                  ),
                ],
              ),
814
            ),
815 816
          ],
        ),
817 818 819 820 821 822 823
      ),
    );

    expect(tester.renderObject(find.byType(Table)), equals(table));
    expect(table.row(0).length, 3);

    await tester.pumpWidget(
824
      Directionality(
825
        textDirection: TextDirection.ltr,
826
        child: Column(
827
          children: <Widget> [
828
            Expanded(
829
              key: tableKey,
830
              child: Table(
831
                children: <TableRow>[
832
                  TableRow(
833
                    children: <Widget>[
834 835
                      Container(key: const ValueKey<int>(1)),
                      Container(key: const ValueKey<int>(2)),
836 837 838 839
                    ],
                  ),
                ],
              ),
840
            ),
841
            Expanded(child: TestStatefulWidget(key: key)),
842 843
          ],
        ),
844 845 846 847 848 849 850
      ),
    );

    expect(tester.renderObject(find.byType(Table)), equals(table));
    expect(table.row(0).length, 2);
  });

851 852
  testWidgets('Table widget diagnostics', (WidgetTester tester) async {
    GlobalKey key0;
853
    final Widget table = Directionality(
Ian Hickson's avatar
Ian Hickson committed
854
      textDirection: TextDirection.ltr,
855 856
      child: Table(
        key: key0 = GlobalKey(),
857
        defaultColumnWidth: const IntrinsicColumnWidth(),
858
        children: const <TableRow>[
859 860 861
          TableRow(
            children: <Widget>[
              Text('A'), Text('B'), Text('C'),
Ian Hickson's avatar
Ian Hickson committed
862
            ],
863
          ),
864 865 866
          TableRow(
            children: <Widget>[
              Text('D'), Text('EEE'), Text('F'),
Ian Hickson's avatar
Ian Hickson committed
867
            ],
868
          ),
869 870 871
          TableRow(
            children: <Widget>[
              Text('G'), Text('H'), Text('III'),
Ian Hickson's avatar
Ian Hickson committed
872
            ],
873
          ),
Ian Hickson's avatar
Ian Hickson committed
874 875 876
        ],
      ),
    );
877
    await tester.pumpWidget(table);
878
    final RenderObjectElement element = key0.currentContext! as RenderObjectElement;
879 880
    expect(element, hasAGoodToStringDeep);
    expect(
881
      element.toStringDeep(minLevel: DiagnosticLevel.info),
882
      equalsIgnoringHashCodes(
883 884
        'Table-[GlobalKey#00000](dependencies: [Directionality, MediaQuery], renderObject: RenderTable#00000)\n'
        '├Text("A", dependencies: [MediaQuery])\n'
885
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "A", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
886
        '├Text("B", dependencies: [MediaQuery])\n'
887
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "B", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
888
        '├Text("C", dependencies: [MediaQuery])\n'
889
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "C", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
890
        '├Text("D", dependencies: [MediaQuery])\n'
891
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "D", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
892
        '├Text("EEE", dependencies: [MediaQuery])\n'
893
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "EEE", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
894
        '├Text("F", dependencies: [MediaQuery])\n'
895
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "F", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
896
        '├Text("G", dependencies: [MediaQuery])\n'
897
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "G", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
898
        '├Text("H", dependencies: [MediaQuery])\n'
899
        '│└RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "H", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n'
900
        '└Text("III", dependencies: [MediaQuery])\n'
901
        ' └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "III", dependencies: [Directionality], renderObject: RenderParagraph#00000 relayoutBoundary=up1)\n',
902 903
      ),
    );
904 905
  });

906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929
  // Regression test for https://github.com/flutter/flutter/issues/31473.
  testWidgets(
    'Does not crash if a child RenderObject is replaced by another RenderObject of a different type',
    (WidgetTester tester) async {
      await tester.pumpWidget(
        Directionality(
          textDirection: TextDirection.ltr,
          child: Table(children: const <TableRow>[TableRow(children: <Widget>[TestChildWidget()])]),
        ),
      );
      expect(find.text('CRASHHH'), findsNothing);

      final TestChildState state = tester.state(find.byType(TestChildWidget));
      state.toggleMe();

      await tester.pumpWidget(
        Directionality(
          textDirection: TextDirection.ltr,
          child: Table(children: const <TableRow>[TableRow(children: <Widget>[TestChildWidget()])]),
        ),
      );

      // Should not crash.
      expect(find.text('CRASHHH'), findsOneWidget);
930
    },
931 932
  );

933 934 935 936 937 938 939 940 941
  testWidgets('Table widget - Default textBaseline is null', (WidgetTester tester) async {
    expect(
      () => Table(defaultVerticalAlignment: TableCellVerticalAlignment.baseline),
      throwsA(
        isAssertionError
          .having((AssertionError error) => error.message, 'exception message', contains('baseline')),
      ),
    );
  });
942

943
  testWidgets(
944
    'Table widget requires all TableRows to have same number of children',
945
    (WidgetTester tester) async {
946
      FlutterError? error;
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962
      try {
        await tester.pumpWidget(
          Directionality(
            textDirection: TextDirection.ltr,
            child: Table(
              children: const <TableRow>[
                TableRow(children: <Widget>[Text('Some Text')]),
                TableRow(),
              ],
            ),
          ),
        );
      } on FlutterError catch (e) {
        error = e;
      } finally {
        expect(error, isNotNull);
963
        expect(error!.toStringDeep(), contains('Table contains irregular row lengths.'));
964
      }
965 966
    },
  );
967

968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
  testWidgets('Can replace child with a different RenderObject type', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/69395.
    await tester.pumpWidget(
      Directionality(
        textDirection: TextDirection.ltr,
        child: Table(children: const <TableRow>[
          TableRow(children: <Widget>[
            TestChildWidget(),
            TestChildWidget(),
            TestChildWidget(),
          ]),
          TableRow(children: <Widget>[
            TestChildWidget(),
            TestChildWidget(),
            TestChildWidget(),
          ]),
        ]),
      ),
    );
    final RenderTable table = tester.renderObject(find.byType(Table));

    expect(find.text('CRASHHH'), findsNothing);
    expect(find.byType(SizedBox), findsNWidgets(3 * 2));
    final Type toBeReplaced = table.column(2).last.runtimeType;

    final TestChildState state = tester.state(find.byType(TestChildWidget).last);
    state.toggleMe();
    await tester.pump();

    expect(find.byType(SizedBox), findsNWidgets(5));
    expect(find.text('CRASHHH'), findsOneWidget);

    // The RenderObject got replaced by a different type.
    expect(table.column(2).last.runtimeType, isNot(toBeReplaced));
  });

1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
  testWidgets('Do not crash if a child that has not been layed out in a previous build is removed', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/60488.
    Widget buildTable(Key key) {
      return Directionality(
        textDirection: TextDirection.ltr,
        child: Table(
          children: <TableRow>[
            TableRow(
              children: <Widget>[
                KeyedSubtree(
                  key: key,
                  child: const Text('Hello'),
                ),
              ],
            ),
          ],
        ),
      );
    }

    await tester.pumpWidget(
      buildTable(const ValueKey<int>(1)),
      null, EnginePhase.build, // Children are not layed out!
    );

    await tester.pumpWidget(
      buildTable(const ValueKey<int>(2)),
    );

    expect(tester.takeException(), isNull);
    expect(find.text('Hello'), findsOneWidget);
  });

1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
  testWidgets('TableRow with no children throws an error message', (WidgetTester tester) async {
    // Regression test for https://github.com/flutter/flutter/issues/119541.
    String result = 'no exception';

    // Test TableRow with children.
    try {
      await tester.pumpWidget(Directionality(
        textDirection: TextDirection.ltr,
        child: Table(
          children: const <TableRow>[
            TableRow(
              children: <Widget>[
                Text('A'),
              ],
            ),
          ],
        ),
      ));
    } on FlutterError catch (e) {
      result = e.toString();
    }

    expect(result, 'no exception');

    // Test TableRow with no children.
    try {
      await tester.pumpWidget(Directionality(
        textDirection: TextDirection.ltr,
        child: Table(
          children: const <TableRow>[
            TableRow(),
          ],
        ),
      ));
    } on FlutterError catch (e) {
      result = e.toString();
    }

    expect(
      result,
      'One or more TableRow have no children.\n'
      'Every TableRow in a Table must have at least one child, so there is no empty row.',
    );
  });

Hixie's avatar
Hixie committed
1082 1083
  // TODO(ianh): Test handling of TableCell object
}