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

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

8 9
import '../rendering/mock_canvas.dart';

10 11 12 13 14 15
enum RadiusType {
  Sharp,
  Shifting,
  Round
}

16 17
void matches(BorderRadius? borderRadius, RadiusType top, RadiusType bottom) {
  final Radius cardRadius = kMaterialEdges[MaterialType.card]!.topLeft;
18 19

  if (top == RadiusType.Sharp) {
20 21
    expect(borderRadius?.topLeft, equals(Radius.zero));
    expect(borderRadius?.topRight, equals(Radius.zero));
22
  } else if (top == RadiusType.Shifting) {
23 24 25 26 27 28 29 30
    expect(borderRadius?.topLeft.x, greaterThan(0.0));
    expect(borderRadius?.topLeft.x, lessThan(cardRadius.x));
    expect(borderRadius?.topLeft.y, greaterThan(0.0));
    expect(borderRadius?.topLeft.y, lessThan(cardRadius.y));
    expect(borderRadius?.topRight.x, greaterThan(0.0));
    expect(borderRadius?.topRight.x, lessThan(cardRadius.x));
    expect(borderRadius?.topRight.y, greaterThan(0.0));
    expect(borderRadius?.topRight.y, lessThan(cardRadius.y));
31
  } else {
32 33
    expect(borderRadius?.topLeft, equals(cardRadius));
    expect(borderRadius?.topRight, equals(cardRadius));
34 35 36
  }

  if (bottom == RadiusType.Sharp) {
37 38
    expect(borderRadius?.bottomLeft, equals(Radius.zero));
    expect(borderRadius?.bottomRight, equals(Radius.zero));
39
  } else if (bottom == RadiusType.Shifting) {
40 41 42 43 44 45 46 47
    expect(borderRadius?.bottomLeft.x, greaterThan(0.0));
    expect(borderRadius?.bottomLeft.x, lessThan(cardRadius.x));
    expect(borderRadius?.bottomLeft.y, greaterThan(0.0));
    expect(borderRadius?.bottomLeft.y, lessThan(cardRadius.y));
    expect(borderRadius?.bottomRight.x, greaterThan(0.0));
    expect(borderRadius?.bottomRight.x, lessThan(cardRadius.x));
    expect(borderRadius?.bottomRight.y, greaterThan(0.0));
    expect(borderRadius?.bottomRight.y, lessThan(cardRadius.y));
48
  } else {
49 50
    expect(borderRadius?.bottomLeft, equals(cardRadius));
    expect(borderRadius?.bottomRight, equals(cardRadius));
51 52 53
  }
}

54 55 56
// Returns the border radius decoration of an item within a MergeableMaterial.
// This depends on the exact structure of objects built by the Material and
// MergeableMaterial widgets.
57
BorderRadius? getBorderRadius(WidgetTester tester, int index) {
58
  final List<Element> containers = tester.elementList(find.byType(Container))
59 60
                                   .toList();

61
  final Container container = containers[index].widget as Container;
62
  final BoxDecoration? boxDecoration = container.decoration as BoxDecoration?;
63

64
  return boxDecoration!.borderRadius as BorderRadius?;
65 66 67 68 69
}

void main() {
  testWidgets('MergeableMaterial empty', (WidgetTester tester) async {
    await tester.pumpWidget(
70
      const MaterialApp(
71 72
        home: Scaffold(
          body: SingleChildScrollView(
73
            child: MergeableMaterial(),
74 75 76
          ),
        ),
      ),
77 78
    );

79
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
80 81 82 83 84
    expect(box.size.height, equals(0));
  });

  testWidgets('MergeableMaterial update slice', (WidgetTester tester) async {
    await tester.pumpWidget(
85
      const MaterialApp(
86 87
        home: Scaffold(
          body: SingleChildScrollView(
88
            child: MergeableMaterial(
89 90 91 92
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
93
                    width: 100.0,
94
                    height: 100.0,
95 96 97 98 99 100 101
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
102 103 104 105 106 107
    );

    RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
    expect(box.size.height, equals(100.0));

    await tester.pumpWidget(
108
      const MaterialApp(
109 110
        home: Scaffold(
          body: SingleChildScrollView(
111
            child: MergeableMaterial(
112 113 114 115
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
116
                    width: 100.0,
117
                    height: 200.0,
118 119 120 121 122 123 124
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
125 126 127 128 129 130 131 132
    );

    box = tester.renderObject(find.byType(MergeableMaterial));
    expect(box.size.height, equals(200.0));
  });

  testWidgets('MergeableMaterial swap slices', (WidgetTester tester) async {
    await tester.pumpWidget(
133
      const MaterialApp(
134 135
        home: Scaffold(
          body: SingleChildScrollView(
136
            child: MergeableMaterial(
137 138 139 140
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
141
                    width: 100.0,
142 143
                    height: 100.0,
                  ),
144
                ),
145 146 147
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
148
                    width: 100.0,
149
                    height: 100.0,
150 151 152 153 154 155 156
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
157 158 159 160 161
    );

    RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
    expect(box.size.height, equals(200.0));

162 163
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
164 165

    await tester.pumpWidget(
166
      const MaterialApp(
167 168
        home: Scaffold(
          body: SingleChildScrollView(
169
            child: MergeableMaterial(
170 171 172 173
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
174
                    width: 100.0,
175 176
                    height: 100.0,
                  ),
177
                ),
178 179 180
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
181
                    width: 100.0,
182
                    height: 100.0,
183 184 185 186 187 188 189
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
190 191 192 193 194 195 196 197
    );

    box = tester.renderObject(find.byType(MergeableMaterial));
    expect(box.size.height, equals(200.0));

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(200.0));

198 199
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
200 201 202
  });

  testWidgets('MergeableMaterial paints shadows', (WidgetTester tester) async {
203
    debugDisableShadows = false;
204
    await tester.pumpWidget(
205
      const MaterialApp(
206 207
        home: Scaffold(
          body: SingleChildScrollView(
208
            child: MergeableMaterial(
209 210 211 212
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
213
                    width: 100.0,
214
                    height: 100.0,
215 216 217 218 219 220 221
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
222 223
    );

224
    final RRect rrect = kMaterialEdges[MaterialType.card]!.toRRect(
225
      const Rect.fromLTRB(0.0, 0.0, 800.0, 100.0),
226
    );
227 228
    expect(
      find.byType(MergeableMaterial),
229 230 231
      paints
        ..shadow(elevation: 2.0)
        ..rrect(rrect: rrect, color: Colors.white, hasMaskFilter: false),
232
    );
233
    debugDisableShadows = true;
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
  testWidgets('MergeableMaterial skips shadow for zero elevation', (WidgetTester tester) async {
    debugDisableShadows = false;
    await tester.pumpWidget(
      const MaterialApp(
        home: Scaffold(
          body: SingleChildScrollView(
            child: MergeableMaterial(
              elevation: 0,
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );

    expect(
      find.byType(MergeableMaterial),
      isNot(paints..shadow(elevation: 0.0)),
    );
    debugDisableShadows = true;
  });

266 267
  testWidgets('MergeableMaterial merge gap', (WidgetTester tester) async {
    await tester.pumpWidget(
268
      const MaterialApp(
269 270
        home: Scaffold(
          body: SingleChildScrollView(
271
            child: MergeableMaterial(
272 273 274 275
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
276
                    width: 100.0,
277 278
                    height: 100.0,
                  ),
279
                ),
280
                MaterialGap(
281
                  key: ValueKey<String>('x'),
282
                ),
283 284 285
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
286
                    width: 100.0,
287
                    height: 100.0,
288 289 290 291 292 293 294
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
295 296
    );

297
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
298 299 300 301 302 303
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);

    await tester.pumpWidget(
304
      const MaterialApp(
305 306
        home: Scaffold(
          body: SingleChildScrollView(
307
            child: MergeableMaterial(
308 309 310 311
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
312
                    width: 100.0,
313 314
                    height: 100.0,
                  ),
315
                ),
316 317 318
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
319
                    width: 100.0,
320
                    height: 100.0,
321 322 323 324 325 326 327
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(200));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
  });

  testWidgets('MergeableMaterial separate slices', (WidgetTester tester) async {
    await tester.pumpWidget(
345
      const MaterialApp(
346 347
        home: Scaffold(
          body: SingleChildScrollView(
348
            child: MergeableMaterial(
349 350 351 352
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
353
                    width: 100.0,
354 355
                    height: 100.0,
                  ),
356
                ),
357 358 359
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
360
                    width: 100.0,
361
                    height: 100.0,
362 363 364 365 366 367 368
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
369 370
    );

371
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
372 373
    expect(box.size.height, equals(200));

374 375
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
376 377

    await tester.pumpWidget(
378
      const MaterialApp(
379 380
        home: Scaffold(
          body: SingleChildScrollView(
381
            child: MergeableMaterial(
382 383 384 385
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
386
                    width: 100.0,
387 388
                    height: 100.0,
                  ),
389
                ),
390
                MaterialGap(
391
                  key: ValueKey<String>('x'),
392
                ),
393 394 395
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
396
                    width: 100.0,
397
                    height: 100.0,
398 399 400 401 402 403 404
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
  });

Josh Soref's avatar
Josh Soref committed
420
  testWidgets('MergeableMaterial separate merge separate', (WidgetTester tester) async {
421
    await tester.pumpWidget(
422
      const MaterialApp(
423 424
        home: Scaffold(
          body: SingleChildScrollView(
425
            child: MergeableMaterial(
426 427 428 429
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
430
                    width: 100.0,
431 432
                    height: 100.0,
                  ),
433
                ),
434 435 436
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
437
                    width: 100.0,
438 439 440
                    height: 100.0,
                  ),
                ),
441
              ],
442 443 444
            ),
          ),
        ),
445
      ),
446 447
    );

448
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
449 450
    expect(box.size.height, equals(200));

451 452 453
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);

454 455

    await tester.pumpWidget(
456
      const MaterialApp(
457 458
        home: Scaffold(
          body: SingleChildScrollView(
459
            child: MergeableMaterial(
460 461 462 463
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
464
                    width: 100.0,
465 466
                    height: 100.0,
                  ),
467
                ),
468
                MaterialGap(
469
                  key: ValueKey<String>('x'),
470
                ),
471 472 473
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
474
                    width: 100.0,
475 476 477
                    height: 100.0,
                  ),
                ),
478
              ],
479 480 481
            ),
          ),
        ),
482
      ),
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);

    await tester.pumpWidget(
498
      const MaterialApp(
499 500
        home: Scaffold(
          body: SingleChildScrollView(
501
            child: MergeableMaterial(
502 503 504 505
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
506
                    width: 100.0,
507 508
                    height: 100.0,
                  ),
509
                ),
510 511 512
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
513
                    width: 100.0,
514 515 516
                    height: 100.0,
                  ),
                ),
517
              ],
518 519 520
            ),
          ),
        ),
521
      ),
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(200));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);

    await tester.pumpWidget(
537
      const MaterialApp(
538 539
        home: Scaffold(
          body: SingleChildScrollView(
540
            child: MergeableMaterial(
541 542 543 544
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
545
                    width: 100.0,
546 547
                    height: 100.0,
                  ),
548
                ),
549
                MaterialGap(
550
                  key: ValueKey<String>('x'),
551
                ),
552 553 554
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
555
                    width: 100.0,
556 557 558
                    height: 100.0,
                  ),
                ),
559
              ],
560 561 562
            ),
          ),
        ),
563
      ),
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
  });

  testWidgets('MergeableMaterial insert slice', (WidgetTester tester) async {
    await tester.pumpWidget(
581
      const MaterialApp(
582 583
        home: Scaffold(
          body: SingleChildScrollView(
584
            child: MergeableMaterial(
585 586 587 588
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
589
                    width: 100.0,
590 591
                    height: 100.0,
                  ),
592
                ),
593 594 595
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
596
                    width: 100.0,
597 598 599
                    height: 100.0,
                  ),
                ),
600
              ],
601 602 603
            ),
          ),
        ),
604
      ),
605 606
    );

607
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
608 609
    expect(box.size.height, equals(200));

610 611
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
612 613

    await tester.pumpWidget(
614
      const MaterialApp(
615 616
        home: Scaffold(
          body: SingleChildScrollView(
617
            child: MergeableMaterial(
618 619 620 621
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
622
                    width: 100.0,
623 624
                    height: 100.0,
                  ),
625
                ),
626 627 628
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
629
                    width: 100.0,
630 631
                    height: 100.0,
                  ),
632
                ),
633 634 635
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
636
                    width: 100.0,
637 638 639
                    height: 100.0,
                  ),
                ),
640
              ],
641 642 643
            ),
          ),
        ),
644
      ),
645 646 647 648
    );

    expect(box.size.height, equals(300));

649 650 651
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Sharp);
    matches(getBorderRadius(tester, 2), RadiusType.Sharp, RadiusType.Round);
652 653 654 655
  });

  testWidgets('MergeableMaterial remove slice', (WidgetTester tester) async {
    await tester.pumpWidget(
656
      const MaterialApp(
657 658
        home: Scaffold(
          body: SingleChildScrollView(
659
            child: MergeableMaterial(
660 661 662 663
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
664
                    width: 100.0,
665 666
                    height: 100.0,
                  ),
667
                ),
668 669 670
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
671
                    width: 100.0,
672 673
                    height: 100.0,
                  ),
674
                ),
675 676 677
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
678
                    width: 100.0,
679 680 681
                    height: 100.0,
                  ),
                ),
682
              ],
683 684 685
            ),
          ),
        ),
686
      ),
687 688
    );

689
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
690 691
    expect(box.size.height, equals(300));

692 693 694
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Sharp);
    matches(getBorderRadius(tester, 2), RadiusType.Sharp, RadiusType.Round);
695 696

    await tester.pumpWidget(
697
      const MaterialApp(
698 699
        home: Scaffold(
          body: SingleChildScrollView(
700
            child: MergeableMaterial(
701 702 703 704
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
705
                    width: 100.0,
706 707
                    height: 100.0,
                  ),
708
                ),
709 710 711
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
712
                    width: 100.0,
713 714 715
                    height: 100.0,
                  ),
                ),
716
              ],
717 718 719
            ),
          ),
        ),
720
      ),
721 722 723 724 725
    );

    await tester.pump();
    expect(box.size.height, equals(200));

726 727
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
728 729 730 731
  });

  testWidgets('MergeableMaterial insert chunk', (WidgetTester tester) async {
    await tester.pumpWidget(
732
      const MaterialApp(
733 734
        home: Scaffold(
          body: SingleChildScrollView(
735
            child: MergeableMaterial(
736 737 738 739
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
740
                    width: 100.0,
741 742
                    height: 100.0,
                  ),
743
                ),
744 745 746
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
747
                    width: 100.0,
748 749 750
                    height: 100.0,
                  ),
                ),
751
              ],
752 753 754
            ),
          ),
        ),
755
      ),
756 757
    );

758
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
759 760
    expect(box.size.height, equals(200));

761 762
    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
763 764

    await tester.pumpWidget(
765
      const MaterialApp(
766 767
        home: Scaffold(
          body: SingleChildScrollView(
768
            child: MergeableMaterial(
769 770 771 772
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
773
                    width: 100.0,
774 775
                    height: 100.0,
                  ),
776
                ),
777
                MaterialGap(
778
                  key: ValueKey<String>('x'),
779
                ),
780 781 782
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
783
                    width: 100.0,
784 785
                    height: 100.0,
                  ),
786
                ),
787
                MaterialGap(
788
                  key: ValueKey<String>('y'),
789
                ),
790 791 792
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
793
                    width: 100.0,
794 795 796
                    height: 100.0,
                  ),
                ),
797
              ],
798 799 800
            ),
          ),
        ),
801
      ),
802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Shifting);
    matches(getBorderRadius(tester, 2), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 2), RadiusType.Round, RadiusType.Round);
  });

  testWidgets('MergeableMaterial remove chunk', (WidgetTester tester) async {
    await tester.pumpWidget(
821
      const MaterialApp(
822 823
        home: Scaffold(
          body: SingleChildScrollView(
824
            child: MergeableMaterial(
825 826 827 828
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
829
                    width: 100.0,
830 831
                    height: 100.0,
                  ),
832
                ),
833
                MaterialGap(
834
                  key: ValueKey<String>('x'),
835
                ),
836 837 838
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
839
                    width: 100.0,
840 841
                    height: 100.0,
                  ),
842
                ),
843
                MaterialGap(
844
                  key: ValueKey<String>('y'),
845
                ),
846 847 848
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
849
                    width: 100.0,
850 851 852
                    height: 100.0,
                  ),
                ),
853
              ],
854 855 856
            ),
          ),
        ),
857
      ),
858 859
    );

860
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
861 862 863 864 865 866 867
    expect(box.size.height, equals(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 2), RadiusType.Round, RadiusType.Round);

    await tester.pumpWidget(
868
      const MaterialApp(
869 870
        home: Scaffold(
          body: SingleChildScrollView(
871
            child: MergeableMaterial(
872 873 874 875
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
876
                    width: 100.0,
877 878
                    height: 100.0,
                  ),
879
                ),
880 881 882
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
883
                    width: 100.0,
884 885 886
                    height: 100.0,
                  ),
                ),
887
              ],
888 889 890
            ),
          ),
        ),
891
      ),
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(200));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Sharp);
    matches(getBorderRadius(tester, 1), RadiusType.Sharp, RadiusType.Round);
  });

  testWidgets('MergeableMaterial replace gap with chunk', (WidgetTester tester) async {
    await tester.pumpWidget(
909
      const MaterialApp(
910 911
        home: Scaffold(
          body: SingleChildScrollView(
912
            child: MergeableMaterial(
913 914 915 916
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
917
                    width: 100.0,
918 919
                    height: 100.0,
                  ),
920
                ),
921
                MaterialGap(
922
                  key: ValueKey<String>('x'),
923
                ),
924 925 926
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
927
                    width: 100.0,
928 929 930
                    height: 100.0,
                  ),
                ),
931
              ],
932 933 934
            ),
          ),
        ),
935
      ),
936 937
    );

938
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
939 940 941 942 943 944
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);

    await tester.pumpWidget(
945
      const MaterialApp(
946 947
        home: Scaffold(
          body: SingleChildScrollView(
948
            child: MergeableMaterial(
949 950 951 952
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
953
                    width: 100.0,
954 955
                    height: 100.0,
                  ),
956
                ),
957
                MaterialGap(
958
                  key: ValueKey<String>('y'),
959
                ),
960 961 962
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
963
                    width: 100.0,
964 965
                    height: 100.0,
                  ),
966
                ),
967
                MaterialGap(
968
                  key: ValueKey<String>('z'),
969
                ),
970 971 972
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
973
                    width: 100.0,
974 975 976
                    height: 100.0,
                  ),
                ),
977
              ],
978 979 980
            ),
          ),
        ),
981
      ),
982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Shifting);
    matches(getBorderRadius(tester, 2), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 2), RadiusType.Round, RadiusType.Round);
  });

  testWidgets('MergeableMaterial replace chunk with gap', (WidgetTester tester) async {
    await tester.pumpWidget(
1001
      const MaterialApp(
1002 1003
        home: Scaffold(
          body: SingleChildScrollView(
1004
            child: MergeableMaterial(
1005 1006 1007 1008
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
1009
                    width: 100.0,
1010 1011
                    height: 100.0,
                  ),
1012
                ),
1013
                MaterialGap(
1014
                  key: ValueKey<String>('x'),
1015
                ),
1016 1017 1018
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
1019
                    width: 100.0,
1020 1021
                    height: 100.0,
                  ),
1022
                ),
1023
                MaterialGap(
1024
                  key: ValueKey<String>('y'),
1025
                ),
1026 1027 1028
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
1029
                    width: 100.0,
1030 1031 1032
                    height: 100.0,
                  ),
                ),
1033
              ],
1034 1035 1036
            ),
          ),
        ),
1037
      ),
1038 1039
    );

1040
    final RenderBox box = tester.renderObject(find.byType(MergeableMaterial));
1041 1042 1043 1044 1045 1046 1047
    expect(box.size.height, equals(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 2), RadiusType.Round, RadiusType.Round);

    await tester.pumpWidget(
1048
      const MaterialApp(
1049 1050
        home: Scaffold(
          body: SingleChildScrollView(
1051
            child: MergeableMaterial(
1052 1053 1054 1055
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
1056
                    width: 100.0,
1057 1058
                    height: 100.0,
                  ),
1059
                ),
1060
                MaterialGap(
1061
                  key: ValueKey<String>('z'),
1062
                ),
1063 1064 1065
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
1066
                    width: 100.0,
1067 1068 1069
                    height: 100.0,
                  ),
                ),
1070
              ],
1071 1072 1073
            ),
          ),
        ),
1074
      ),
1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
    );

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, lessThan(332));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Shifting);
    matches(getBorderRadius(tester, 1), RadiusType.Shifting, RadiusType.Round);

    await tester.pump(const Duration(milliseconds: 100));
    expect(box.size.height, equals(216));

    matches(getBorderRadius(tester, 0), RadiusType.Round, RadiusType.Round);
    matches(getBorderRadius(tester, 1), RadiusType.Round, RadiusType.Round);
  });
1089

1090
  bool isDivider(BoxDecoration decoration, bool top, bool bottom) {
1091
    const BorderSide side = BorderSide(color: Color(0x1F000000), width: 0.5);
1092

1093
    return decoration == BoxDecoration(
1094
      border: Border(
1095
        top: top ? side : BorderSide.none,
1096 1097
        bottom: bottom ? side : BorderSide.none,
      ),
1098 1099 1100 1101 1102
    );
  }

  testWidgets('MergeableMaterial dividers', (WidgetTester tester) async {
    await tester.pumpWidget(
1103
      const MaterialApp(
1104 1105
        home: Scaffold(
          body: SingleChildScrollView(
1106
            child: MergeableMaterial(
1107
              hasDividers: true,
1108 1109 1110 1111
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
1112
                    width: 100.0,
1113 1114
                    height: 100.0,
                  ),
1115
                ),
1116 1117 1118
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
1119
                    width: 100.0,
1120 1121
                    height: 100.0,
                  ),
1122
                ),
1123 1124 1125
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
1126
                    width: 100.0,
1127 1128
                    height: 100.0,
                  ),
1129
                ),
1130 1131 1132
                MaterialSlice(
                  key: ValueKey<String>('D'),
                  child: SizedBox(
1133
                    width: 100.0,
1134 1135 1136 1137 1138 1139 1140
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
1141
      ),
1142 1143
    );

1144 1145 1146 1147 1148 1149 1150 1151 1152
    List<Widget> animatedContainers = tester.widgetList(
      find.byType(AnimatedContainer),
    ).toList();
    List<BoxDecoration> boxes = <BoxDecoration>[];
    for (final Widget container in animatedContainers) {
      boxes.add((container as AnimatedContainer).decoration! as BoxDecoration);
    }

    int offset = 0;
1153

1154 1155 1156 1157
    expect(isDivider(boxes[offset], false, true), isTrue);
    expect(isDivider(boxes[offset + 1], true, true), isTrue);
    expect(isDivider(boxes[offset + 2], true, true), isTrue);
    expect(isDivider(boxes[offset + 3], true, false), isTrue);
1158 1159

    await tester.pumpWidget(
1160
      const MaterialApp(
1161 1162
        home: Scaffold(
          body: SingleChildScrollView(
1163
            child: MergeableMaterial(
1164
              hasDividers: true,
1165 1166 1167 1168
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
1169
                    width: 100.0,
1170 1171
                    height: 100.0,
                  ),
1172
                ),
1173 1174 1175
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
1176
                    width: 100.0,
1177 1178
                    height: 100.0,
                  ),
1179
                ),
1180
                MaterialGap(
1181
                  key: ValueKey<String>('x'),
1182
                ),
1183 1184 1185
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
1186
                    width: 100.0,
1187 1188
                    height: 100.0,
                  ),
1189
                ),
1190 1191 1192
                MaterialSlice(
                  key: ValueKey<String>('D'),
                  child: SizedBox(
1193
                    width: 100.0,
1194 1195 1196 1197 1198 1199 1200
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
1201
      ),
1202 1203
    );

1204 1205 1206
    // Wait for dividers to shrink.
    await tester.pump(const Duration(milliseconds: 200));

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
    animatedContainers = tester.widgetList(
      find.byType(AnimatedContainer),
    ).toList();
    boxes = <BoxDecoration>[];

    for (final Widget container in animatedContainers) {
      boxes.add((container as AnimatedContainer).decoration! as BoxDecoration);
    }

    offset = 0;
1217

1218 1219
    expect(isDivider(boxes[offset], false, true), isTrue);
    expect(isDivider(boxes[offset + 1], true, false), isTrue);
1220 1221
    expect(isDivider(boxes[offset + 2], false, true), isTrue);
    expect(isDivider(boxes[offset + 3], true, false), isTrue);
1222
  });
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257

  testWidgets('MergeableMaterial respects dividerColor', (WidgetTester tester) async {
    const Color dividerColor = Colors.red;
    await tester.pumpWidget(
      const MaterialApp(
        home: Scaffold(
          body: SingleChildScrollView(
            child: MergeableMaterial(
              hasDividers: true,
              dividerColor: dividerColor,
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  child: SizedBox(
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
                MaterialSlice(
                  key: ValueKey<String>('B'),
                  child: SizedBox(
                    width: 100.0,
                    height: 100.0,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );

    final DecoratedBox decoratedBox = tester.widget(find.byType(DecoratedBox).last);
    final BoxDecoration decoration = decoratedBox.decoration as BoxDecoration;
    // Since we are getting the last DecoratedBox, it will have a Border.top.
1258
    expect(decoration.border!.top.color, dividerColor);
1259
  });
1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304

  testWidgets('MergeableMaterial respects MaterialSlice.color', (WidgetTester tester) async {
    const Color themeCardColor = Colors.red;
    const Color materialSliceColor = Colors.green;

    await tester.pumpWidget(
      MaterialApp(
        theme: ThemeData(
          cardColor: themeCardColor,
        ),
        home: const Scaffold(
          body: SingleChildScrollView(
            child: MergeableMaterial(
              children: <MergeableMaterialItem>[
                MaterialSlice(
                  key: ValueKey<String>('A'),
                  color: materialSliceColor,
                  child: SizedBox(
                    height: 100,
                    width: 100,
                  ),
                ),
                MaterialGap(
                  key: ValueKey<String>('B'),
                ),
                MaterialSlice(
                  key: ValueKey<String>('C'),
                  child: SizedBox(
                    height: 100,
                    width: 100,
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );

    BoxDecoration boxDecoration = tester.widget<Container>(find.byType(Container).first).decoration! as BoxDecoration;
    expect(boxDecoration.color, materialSliceColor);

    boxDecoration = tester.widget<Container>(find.byType(Container).last).decoration! as BoxDecoration;
    expect(boxDecoration.color, themeCardColor);
  });
1305
}