persistent_bottom_sheet_test.dart 22.1 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5
// 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';
6
import 'package:flutter/rendering.dart';
7
import 'package:flutter_test/flutter_test.dart';
8
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
9

10
void main() {
11
  // Pumps and ensures that the BottomSheet animates non-linearly.
12
  Future<void> checkNonLinearAnimation(WidgetTester tester) async {
13 14 15 16 17 18 19 20 21 22
    final Offset firstPosition = tester.getCenter(find.text('One'));
    await tester.pump(const Duration(milliseconds: 30));
    final Offset secondPosition = tester.getCenter(find.text('One'));
    await tester.pump(const Duration(milliseconds: 30));
    final Offset thirdPosition = tester.getCenter(find.text('One'));

    final double dyDelta1 = secondPosition.dy - firstPosition.dy;
    final double dyDelta2 = thirdPosition.dy - secondPosition.dy;

    // If the animation were linear, these two values would be the same.
23
    expect(dyDelta1, isNot(moreOrLessEquals(dyDelta2, epsilon: 0.1)));
24 25
  }

26
  testWidgetsWithLeakTracking('Persistent draggableScrollableSheet localHistoryEntries test', (WidgetTester tester) async {
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    // Regression test for https://github.com/flutter/flutter/issues/110123
    Widget buildFrame(Widget? bottomSheet) {
      return MaterialApp(
        home: Scaffold(
          appBar: AppBar(),
          body: const Center(child: Text('body')),
          bottomSheet: bottomSheet,
          floatingActionButton: const FloatingActionButton(
            onPressed: null,
            child: Text('fab'),
          ),
        ),
      );
    }
    final Widget draggableScrollableSheet = DraggableScrollableSheet(
      expand: false,
      snap: true,
      initialChildSize: 0.3,
      minChildSize: 0.3,
      builder: (_, ScrollController controller) {
        return ListView.builder(
          itemExtent: 50.0,
          itemCount: 50,
          itemBuilder: (_, int index) => Text('Item $index'),
          controller: controller,
        );
      },
    );

    await tester.pumpWidget(buildFrame(draggableScrollableSheet));
    await tester.pumpAndSettle();

    expect(find.byType(BackButton).hitTestable(), findsNothing);

    await tester.drag(find.text('Item 2'), const Offset(0, -200.0));
    await tester.pumpAndSettle();
    // We've started to drag up, we should have a back button now for a11y
    expect(find.byType(BackButton).hitTestable(), findsOneWidget);

    await tester.fling(find.text('Item 2'), const Offset(0, 200.0), 2000.0);
    await tester.pumpAndSettle();
    // BackButton should be hidden
    expect(find.byType(BackButton).hitTestable(), findsNothing);

    // Show the back button again
    await tester.drag(find.text('Item 2'), const Offset(0, -200.0));
    await tester.pumpAndSettle();
    expect(find.byType(BackButton).hitTestable(), findsOneWidget);

    // Remove the draggableScrollableSheet should hide the back button
    await tester.pumpWidget(buildFrame(null));
    expect(find.byType(BackButton).hitTestable(), findsNothing);
  });

81
  // Regression test for https://github.com/flutter/flutter/issues/83668
82
  testWidgetsWithLeakTracking('Scaffold.bottomSheet update test', (WidgetTester tester) async {
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    Widget buildFrame(Widget? bottomSheet) {
      return MaterialApp(
        home: Scaffold(
          body: const Placeholder(),
          bottomSheet: bottomSheet,
        ),
      );
    }

    await tester.pumpWidget(buildFrame(const Text('I love Flutter!')));
    await tester.pumpWidget(buildFrame(null));

    // The disappearing animation has not yet been completed.
    await tester.pumpWidget(buildFrame(const Text('I love Flutter!')));
  });

99
  testWidgetsWithLeakTracking('Verify that a BottomSheet can be rebuilt with ScaffoldFeatureController.setState()', (WidgetTester tester) async {
100
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
101
    int buildCount = 0;
102

103 104
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
105
        key: scaffoldKey,
106 107
        body: const Center(child: Text('body')),
      ),
108
    ));
109

110
    final PersistentBottomSheetController bottomSheet = scaffoldKey.currentState!.showBottomSheet((_) {
111
      return Builder(
112 113
        builder: (BuildContext context) {
          buildCount += 1;
114
          return Container(height: 200.0);
115
        },
116 117
      );
    });
118

119
    await tester.pump();
120
    expect(buildCount, equals(1));
121
    bottomSheet.setState!(() { });
122
    await tester.pump();
123
    expect(buildCount, equals(2));
124 125
  });

126
  testWidgetsWithLeakTracking('Verify that a persistent BottomSheet cannot be dismissed', (WidgetTester tester) async {
127 128 129 130 131 132 133 134 135
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: const Center(child: Text('body')),
        bottomSheet: DraggableScrollableSheet(
          expand: false,
          builder: (_, ScrollController controller) {
            return ListView(
              controller: controller,
              shrinkWrap: true,
136 137 138 139
              children: const <Widget>[
                SizedBox(height: 100.0, child: Text('One')),
                SizedBox(height: 100.0, child: Text('Two')),
                SizedBox(height: 100.0, child: Text('Three')),
140 141 142 143
              ],
            );
          },
        ),
144
      ),
145 146 147 148 149 150 151 152 153 154 155 156
    ));

    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);

    await tester.drag(find.text('Two'), const Offset(0.0, 400.0));
    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);
  });

157
  testWidgetsWithLeakTracking('Verify that a scrollable BottomSheet can be dismissed', (WidgetTester tester) async {
158
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
159

160 161
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
162
        key: scaffoldKey,
163 164
        body: const Center(child: Text('body')),
      ),
165 166
    ));

167
    scaffoldKey.currentState!.showBottomSheet((BuildContext context) {
168
      return ListView(
169
        shrinkWrap: true,
170
        primary: false,
171 172 173 174
        children: const <Widget>[
          SizedBox(height: 100.0, child: Text('One')),
          SizedBox(height: 100.0, child: Text('Two')),
          SizedBox(height: 100.0, child: Text('Three')),
175 176 177 178
        ],
      );
    });

179
    await tester.pumpAndSettle();
180 181 182

    expect(find.text('Two'), findsOneWidget);

183 184
    await tester.drag(find.text('Two'), const Offset(0.0, 400.0));
    await tester.pumpAndSettle();
185 186 187 188

    expect(find.text('Two'), findsNothing);
  });

189
  testWidgetsWithLeakTracking('Verify DraggableScrollableSheet.shouldCloseOnMinExtent == false prevents dismissal', (WidgetTester tester) async {
190 191 192 193 194 195 196 197 198
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
        body: const Center(child: Text('body')),
      ),
    ));

199
    scaffoldKey.currentState!.showBottomSheet((BuildContext context) {
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
      return DraggableScrollableSheet(
        expand: false,
        shouldCloseOnMinExtent: false,
        builder: (_, ScrollController controller) {
          return ListView(
            controller: controller,
            shrinkWrap: true,
            children: const <Widget>[
              SizedBox(height: 100.0, child: Text('One')),
              SizedBox(height: 100.0, child: Text('Two')),
              SizedBox(height: 100.0, child: Text('Three')),
            ],
          );
        },
      );
    });

    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);

    await tester.drag(find.text('Two'), const Offset(0.0, 400.0));
    await tester.pumpAndSettle();

     expect(find.text('Two'), findsOneWidget);
  });

227
  testWidgetsWithLeakTracking('Verify that a BottomSheet animates non-linearly', (WidgetTester tester) async {
228 229 230 231 232 233 234 235 236
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
        body: const Center(child: Text('body')),
      ),
    ));

237
    scaffoldKey.currentState!.showBottomSheet((BuildContext context) {
238 239 240
      return ListView(
        shrinkWrap: true,
        primary: false,
241 242 243 244
        children: const <Widget>[
          SizedBox(height: 100.0, child: Text('One')),
          SizedBox(height: 100.0, child: Text('Two')),
          SizedBox(height: 100.0, child: Text('Three')),
245 246 247 248
        ],
      );
    });
    await tester.pump();
249
    await checkNonLinearAnimation(tester);
250 251 252 253 254 255

    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);

    await tester.drag(find.text('Two'), const Offset(0.0, 200.0));
256
    await checkNonLinearAnimation(tester);
257 258 259 260 261
    await tester.pumpAndSettle();

    expect(find.text('Two'), findsNothing);
  });

262
  testWidgetsWithLeakTracking('Verify that a scrollControlled BottomSheet can be dismissed', (WidgetTester tester) async {
263 264 265 266 267
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
268
        body: const Center(child: Text('body')),
269
      ),
270 271
    ));

272
    scaffoldKey.currentState!.showBottomSheet(
273 274 275 276 277 278 279
      (BuildContext context) {
        return DraggableScrollableSheet(
          expand: false,
          builder: (_, ScrollController controller) {
            return ListView(
              shrinkWrap: true,
              controller: controller,
280 281 282 283
              children: const <Widget>[
                SizedBox(height: 100.0, child: Text('One')),
                SizedBox(height: 100.0, child: Text('Two')),
                SizedBox(height: 100.0, child: Text('Three')),
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
              ],
            );
          },
        );
      },
    );

    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);

    await tester.drag(find.text('Two'), const Offset(0.0, 400.0));
    await tester.pumpAndSettle();

    expect(find.text('Two'), findsNothing);
  });

301
  testWidgetsWithLeakTracking('Verify that a persistent BottomSheet can fling up and hide the fab', (WidgetTester tester) async {
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          appBar: AppBar(),
          body: const Center(child: Text('body')),
          bottomSheet: DraggableScrollableSheet(
            expand: false,
            builder: (_, ScrollController controller) {
              return ListView.builder(
                itemExtent: 50.0,
                itemCount: 50,
                itemBuilder: (_, int index) => Text('Item $index'),
                controller: controller,
              );
            },
          ),
          floatingActionButton: const FloatingActionButton(
            onPressed: null,
            child: Text('fab'),
          ),
        ),
      ),
    );

    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);
    expect(find.byType(FloatingActionButton), findsOneWidget);
    expect(find.byType(FloatingActionButton).hitTestable(), findsOneWidget);
    expect(find.byType(BackButton).hitTestable(), findsNothing);

    await tester.drag(find.text('Item 2'), const Offset(0, -20.0));
    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);
    expect(find.byType(FloatingActionButton), findsOneWidget);
    expect(find.byType(FloatingActionButton).hitTestable(), findsOneWidget);

    await tester.fling(find.text('Item 2'), const Offset(0.0, -600.0), 2000.0);
    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsNothing);
    expect(find.text('Item 22'), findsOneWidget);
    expect(find.byType(FloatingActionButton), findsOneWidget);
    expect(find.byType(FloatingActionButton).hitTestable(), findsNothing);
  });

351
  testWidgetsWithLeakTracking('Verify that a back button resets a persistent BottomSheet', (WidgetTester tester) async {
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 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
    await tester.pumpWidget(
      MaterialApp(
        home: Scaffold(
          appBar: AppBar(),
          body: const Center(child: Text('body')),
          bottomSheet: DraggableScrollableSheet(
            expand: false,
            builder: (_, ScrollController controller) {
              return ListView.builder(
                itemExtent: 50.0,
                itemCount: 50,
                itemBuilder: (_, int index) => Text('Item $index'),
                controller: controller,
              );
            },
          ),
          floatingActionButton: const FloatingActionButton(
            onPressed: null,
            child: Text('fab'),
          ),
        ),
      ),
    );

    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);
    expect(find.byType(BackButton).hitTestable(), findsNothing);

    await tester.drag(find.text('Item 2'), const Offset(0, -20.0));
    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);
    // We've started to drag up, we should have a back button now for a11y
    expect(find.byType(BackButton).hitTestable(), findsOneWidget);

    await tester.tap(find.byType(BackButton));
    await tester.pumpAndSettle();

    expect(find.byType(BackButton).hitTestable(), findsNothing);
    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);

    await tester.fling(find.text('Item 2'), const Offset(0.0, -600.0), 2000.0);
    await tester.pumpAndSettle();

    expect(find.text('Item 2'), findsNothing);
    expect(find.text('Item 22'), findsOneWidget);
    expect(find.byType(BackButton).hitTestable(), findsOneWidget);

    await tester.tap(find.byType(BackButton));
    await tester.pumpAndSettle();

    expect(find.byType(BackButton).hitTestable(), findsNothing);
    expect(find.text('Item 2'), findsOneWidget);
    expect(find.text('Item 22'), findsNothing);
  });

412
  testWidgetsWithLeakTracking('Verify that a scrollable BottomSheet hides the fab when scrolled up', (WidgetTester tester) async {
413 414 415 416 417 418 419 420 421 422
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
        body: const Center(child: Text('body')),
        floatingActionButton: const FloatingActionButton(
          onPressed: null,
          child: Text('fab'),
        ),
423
      ),
424 425
    ));

426
    scaffoldKey.currentState!.showBottomSheet(
427 428 429 430 431 432 433
      (BuildContext context) {
        return DraggableScrollableSheet(
          expand: false,
          builder: (_, ScrollController controller) {
            return ListView(
              controller: controller,
              shrinkWrap: true,
434 435 436 437 438 439 440 441 442 443 444 445
              children: const <Widget>[
                SizedBox(height: 100.0, child: Text('One')),
                SizedBox(height: 100.0, child: Text('Two')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
                SizedBox(height: 100.0, child: Text('Three')),
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
              ],
            );
          },
        );
      },
    );

    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);
    expect(find.byType(FloatingActionButton).hitTestable(), findsOneWidget);

    await tester.drag(find.text('Two'), const Offset(0.0, -600.0));
    await tester.pumpAndSettle();

    expect(find.text('Two'), findsOneWidget);
    expect(find.byType(FloatingActionButton), findsOneWidget);
    expect(find.byType(FloatingActionButton).hitTestable(), findsNothing);
  });

466
  testWidgetsWithLeakTracking('showBottomSheet()', (WidgetTester tester) async {
467 468 469 470
    final GlobalKey key = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        body: Placeholder(key: key),
471
      ),
472 473 474
    ));

    int buildCount = 0;
475
    showBottomSheet(
476
      context: key.currentContext!,
477
      builder: (BuildContext context) {
478
        return Builder(
479 480
          builder: (BuildContext context) {
            buildCount += 1;
481
            return Container(height: 200.0);
482
          },
483 484 485 486 487 488 489
        );
      },
    );
    await tester.pump();
    expect(buildCount, equals(1));
  });

490
  testWidgetsWithLeakTracking('Scaffold removes top MediaQuery padding', (WidgetTester tester) async {
491 492
    late BuildContext scaffoldContext;
    late BuildContext bottomSheetContext;
493

494 495
    await tester.pumpWidget(MaterialApp(
      home: MediaQuery(
496
        data: const MediaQueryData(
497
          padding: EdgeInsets.all(50.0),
498
        ),
499
        child: Scaffold(
500
          resizeToAvoidBottomInset: false,
501
          body: Builder(
502 503
            builder: (BuildContext context) {
              scaffoldContext = context;
504
              return Container();
505
            },
506 507
          ),
        ),
508
      ),
509 510 511 512
    ));

    await tester.pump();

513
    showBottomSheet(
514 515 516
      context: scaffoldContext,
      builder: (BuildContext context) {
        bottomSheetContext = context;
517
        return Container();
518 519 520 521 522 523
      },
    );

    await tester.pump();

    expect(
524
      MediaQuery.of(bottomSheetContext).padding,
525 526 527 528 529 530 531
      const EdgeInsets.only(
        bottom: 50.0,
        left: 50.0,
        right: 50.0,
      ),
    );
  });
532

533
  testWidgetsWithLeakTracking('Scaffold.bottomSheet', (WidgetTester tester) async {
534
    final Key bottomSheetKey = UniqueKey();
535 536

    await tester.pumpWidget(
537
      MaterialApp(
538
        theme: ThemeData(useMaterial3: false),
539
        home: Scaffold(
540
          body: const Placeholder(),
541
          bottomSheet: Container(
542 543 544
            key: bottomSheetKey,
            alignment: Alignment.center,
            height: 200.0,
545
            child: Builder(
546
              builder: (BuildContext context) {
547
                return ElevatedButton(
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
                  child: const Text('showModalBottomSheet'),
                  onPressed: () {
                    showModalBottomSheet<void>(
                      context: context,
                      builder: (BuildContext context) => const Text('modal bottom sheet'),
                    );
                  },
                );
              },
            ),
          ),
        ),
      ),
    );

    expect(find.text('showModalBottomSheet'), findsOneWidget);
    expect(tester.getSize(find.byKey(bottomSheetKey)), const Size(800.0, 200.0));
    expect(tester.getTopLeft(find.byKey(bottomSheetKey)), const Offset(0.0, 400.0));

    // Show the modal bottomSheet
    await tester.tap(find.text('showModalBottomSheet'));
    await tester.pumpAndSettle();
    expect(find.text('modal bottom sheet'), findsOneWidget);

572 573
    // Dismiss the modal bottomSheet by tapping above the sheet
    await tester.tapAt(const Offset(20.0, 20.0));
574 575 576 577 578 579
    await tester.pumpAndSettle();
    expect(find.text('modal bottom sheet'), findsNothing);
    expect(find.text('showModalBottomSheet'), findsOneWidget);

    // Remove the persistent bottomSheet
    await tester.pumpWidget(
580 581
      const MaterialApp(
        home: Scaffold(
582
          body: Placeholder(),
583 584 585 586 587 588 589
        ),
      ),
    );
    await tester.pumpAndSettle();
    expect(find.text('showModalBottomSheet'), findsNothing);
    expect(find.byKey(bottomSheetKey), findsNothing);
  });
590

591
  // Regression test for https://github.com/flutter/flutter/issues/71435
592
  testWidgetsWithLeakTracking(
593 594 595 596 597 598 599 600 601 602 603
    'Scaffold.bottomSheet should be updated without creating a new RO'
    ' when the new widget has the same key and type.',
    (WidgetTester tester) async {
      Widget buildFrame(String text) {
        return MaterialApp(
          home: Scaffold(
            body: const Placeholder(),
            bottomSheet: Text(text),
          ),
        );
      }
604

605 606
      await tester.pumpWidget(buildFrame('I love Flutter!'));
      final RenderParagraph renderBeforeUpdate = tester.renderObject(find.text('I love Flutter!'));
607

608 609 610
      await tester.pumpWidget(buildFrame('Flutter is the best!'));
      await tester.pumpAndSettle();
      final RenderParagraph renderAfterUpdate = tester.renderObject(find.text('Flutter is the best!'));
611

612 613 614
      expect(renderBeforeUpdate, renderAfterUpdate);
    },
  );
615

616
  testWidgetsWithLeakTracking('Verify that visual properties are passed through', (WidgetTester tester) async {
617 618 619
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();
    const Color color = Colors.pink;
    const double elevation = 9.0;
620
    const ShapeBorder shape = BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12)));
621
    const Clip clipBehavior = Clip.antiAlias;
622 623 624 625 626 627 628 629

    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
        body: const Center(child: Text('body')),
      ),
    ));

630
    scaffoldKey.currentState!.showBottomSheet((BuildContext context) {
631 632 633
      return ListView(
        shrinkWrap: true,
        primary: false,
634 635 636 637
        children: const <Widget>[
          SizedBox(height: 100.0, child: Text('One')),
          SizedBox(height: 100.0, child: Text('Two')),
          SizedBox(height: 100.0, child: Text('Three')),
638 639
        ],
      );
640
    }, backgroundColor: color, elevation: elevation, shape: shape, clipBehavior: clipBehavior);
641 642 643 644 645 646 647

    await tester.pumpAndSettle();

    final BottomSheet bottomSheet = tester.widget(find.byType(BottomSheet));
    expect(bottomSheet.backgroundColor, color);
    expect(bottomSheet.elevation, elevation);
    expect(bottomSheet.shape, shape);
648
    expect(bottomSheet.clipBehavior, clipBehavior);
649 650
  });

651
  testWidgetsWithLeakTracking('PersistentBottomSheetController.close dismisses the bottom sheet', (WidgetTester tester) async {
652 653 654 655
    final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
    await tester.pumpWidget(MaterialApp(
      home: Scaffold(
        key: scaffoldKey,
656
        body: const Center(child: Text('body')),
657
      ),
658 659
    ));

660
    final PersistentBottomSheetController bottomSheet = scaffoldKey.currentState!.showBottomSheet((_) {
661 662 663
      return Builder(
        builder: (BuildContext context) {
          return Container(height: 200.0);
664
        },
665 666 667 668 669 670 671 672 673 674
      );
    });

    await tester.pump();
    expect(find.byType(BottomSheet), findsOneWidget);

    bottomSheet.close();
    await tester.pump();
    expect(find.byType(BottomSheet), findsNothing);
  });
675
}