1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/cupertino.dart';
6import 'package:flutter/material.dart';
7import 'package:flutter/services.dart';
8import 'package:flutter_localizations/flutter_localizations.dart';
9import 'package:flutter_test/flutter_test.dart';
10
11import '../widgets/navigator_utils.dart';
12
13// Matches _kTopGapRatio in cupertino/sheet.dart.
14const double _kTopGapRatio = 0.08;
15
16void main() {
17 testWidgets('Sheet route does not cover the whole screen', (WidgetTester tester) async {
18 final GlobalKey scaffoldKey = GlobalKey();
19
20 await tester.pumpWidget(
21 CupertinoApp(
22 home: CupertinoPageScaffold(
23 key: scaffoldKey,
24 child: Center(
25 child: Column(
26 children: <Widget>[
27 const Text('Page 1'),
28 CupertinoButton(
29 onPressed: () {
30 Navigator.push<void>(
31 scaffoldKey.currentContext!,
32 CupertinoSheetRoute<void>(
33 builder: (BuildContext context) {
34 return const CupertinoPageScaffold(child: Text('Page 2'));
35 },
36 ),
37 );
38 },
39 child: const Text('Push Page 2'),
40 ),
41 ],
42 ),
43 ),
44 ),
45 ),
46 );
47
48 expect(find.text('Page 1'), findsOneWidget);
49 expect(find.text('Page 2'), findsNothing);
50
51 await tester.tap(find.text('Push Page 2'));
52 await tester.pumpAndSettle();
53
54 expect(find.text('Page 2'), findsOneWidget);
55 expect(
56 tester
57 .getTopLeft(
58 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
59 )
60 .dy,
61 greaterThan(0.0),
62 );
63 });
64
65 testWidgets('Previous route moves slight downward when sheet route is pushed', (
66 WidgetTester tester,
67 ) async {
68 final GlobalKey scaffoldKey = GlobalKey();
69
70 await tester.pumpWidget(
71 CupertinoApp(
72 home: CupertinoPageScaffold(
73 key: scaffoldKey,
74 child: Column(
75 children: <Widget>[
76 const Text('Page 1'),
77 CupertinoButton(
78 onPressed: () {
79 Navigator.push<void>(
80 scaffoldKey.currentContext!,
81 CupertinoSheetRoute<void>(
82 builder: (BuildContext context) {
83 return const CupertinoPageScaffold(child: Text('Page 2'));
84 },
85 ),
86 );
87 },
88 child: const Text('Push Page 2'),
89 ),
90 ],
91 ),
92 ),
93 ),
94 );
95
96 expect(find.text('Page 1'), findsOneWidget);
97 expect(
98 tester
99 .getTopLeft(
100 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
101 )
102 .dy,
103 equals(0.0),
104 );
105 expect(find.text('Page 2'), findsNothing);
106
107 await tester.tap(find.text('Push Page 2'));
108 await tester.pumpAndSettle();
109
110 // Previous page is still visible behind the new sheet.
111 expect(find.text('Page 1'), findsOneWidget);
112 final Offset pageOneOffset = tester.getTopLeft(
113 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
114 );
115 expect(pageOneOffset.dy, greaterThan(0.0));
116 expect(pageOneOffset.dx, greaterThan(0.0));
117 expect(find.text('Page 2'), findsOneWidget);
118 final double pageTwoYOffset =
119 tester
120 .getTopLeft(
121 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
122 )
123 .dy;
124 expect(pageTwoYOffset, greaterThan(pageOneOffset.dy));
125 });
126
127 testWidgets('If a sheet covers another sheet, then the previous sheet moves slightly upwards', (
128 WidgetTester tester,
129 ) async {
130 final GlobalKey scaffoldKey = GlobalKey();
131
132 await tester.pumpWidget(
133 CupertinoApp(
134 home: CupertinoPageScaffold(
135 key: scaffoldKey,
136 child: Column(
137 children: <Widget>[
138 const Text('Page 1'),
139 CupertinoButton(
140 onPressed: () {
141 Navigator.push<void>(
142 scaffoldKey.currentContext!,
143 CupertinoSheetRoute<void>(
144 builder: (BuildContext context) {
145 return CupertinoPageScaffold(
146 child: Column(
147 children: <Widget>[
148 const Text('Page 2'),
149 CupertinoButton(
150 onPressed: () {
151 Navigator.push<void>(
152 scaffoldKey.currentContext!,
153 CupertinoSheetRoute<void>(
154 builder: (BuildContext context) {
155 return const CupertinoPageScaffold(child: Text('Page 3'));
156 },
157 ),
158 );
159 },
160 child: const Text('Push Page 3'),
161 ),
162 ],
163 ),
164 );
165 },
166 ),
167 );
168 },
169 child: const Text('Push Page 2'),
170 ),
171 ],
172 ),
173 ),
174 ),
175 );
176
177 expect(find.text('Page 1'), findsOneWidget);
178 expect(
179 tester
180 .getTopLeft(
181 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
182 )
183 .dy,
184 equals(0.0),
185 );
186 expect(find.text('Page 2'), findsNothing);
187 expect(find.text('Page 3'), findsNothing);
188
189 await tester.tap(find.text('Push Page 2'));
190 await tester.pumpAndSettle();
191
192 expect(find.text('Page 2'), findsOneWidget);
193 expect(find.text('Page 3'), findsNothing);
194 final double previousPageTwoDY =
195 tester
196 .getTopLeft(
197 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
198 )
199 .dy;
200
201 await tester.tap(find.text('Push Page 3'));
202 await tester.pumpAndSettle();
203
204 expect(find.text('Page 3'), findsOneWidget);
205 expect(previousPageTwoDY, greaterThan(0.0));
206 expect(
207 previousPageTwoDY,
208 greaterThan(
209 tester
210 .getTopLeft(
211 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
212 )
213 .dy,
214 ),
215 );
216 });
217
218 testWidgets('by default showCupertinoSheet does not enable nested navigation', (
219 WidgetTester tester,
220 ) async {
221 final GlobalKey scaffoldKey = GlobalKey();
222
223 Widget sheetScaffoldContent(BuildContext context) {
224 return Column(
225 children: <Widget>[
226 const Text('Page 2'),
227 CupertinoButton(
228 onPressed: () {
229 Navigator.push<void>(
230 context,
231 CupertinoPageRoute<void>(
232 builder: (BuildContext context) {
233 return CupertinoPageScaffold(
234 child: Column(
235 children: <Widget>[
236 const Text('Page 3'),
237 CupertinoButton(onPressed: () {}, child: const Text('Pop Page 3')),
238 ],
239 ),
240 );
241 },
242 ),
243 );
244 },
245 child: const Text('Push Page 3'),
246 ),
247 ],
248 );
249 }
250
251 await tester.pumpWidget(
252 CupertinoApp(
253 home: CupertinoPageScaffold(
254 key: scaffoldKey,
255 child: Center(
256 child: Column(
257 children: <Widget>[
258 const Text('Page 1'),
259 CupertinoButton(
260 onPressed: () {
261 showCupertinoSheet<void>(
262 context: scaffoldKey.currentContext!,
263 pageBuilder: (BuildContext context) {
264 return CupertinoPageScaffold(child: sheetScaffoldContent(context));
265 },
266 );
267 },
268 child: const Text('Push Page 2'),
269 ),
270 ],
271 ),
272 ),
273 ),
274 ),
275 );
276
277 expect(find.text('Page 1'), findsOneWidget);
278
279 await tester.tap(find.text('Push Page 2'));
280 await tester.pumpAndSettle();
281
282 expect(find.text('Page 2'), findsOneWidget);
283 expect(find.text('Page 3'), findsNothing);
284
285 expect(
286 tester
287 .getTopLeft(
288 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
289 )
290 .dy,
291 greaterThan(0.0),
292 );
293
294 await tester.tap(find.text('Push Page 3'));
295 await tester.pumpAndSettle();
296
297 expect(find.text('Page 2'), findsNothing);
298 expect(find.text('Page 3'), findsOneWidget);
299 // New route should be at the top of the screen.
300 expect(
301 tester
302 .getTopLeft(
303 find.ancestor(of: find.text('Page 3'), matching: find.byType(CupertinoPageScaffold)),
304 )
305 .dy,
306 equals(0.0),
307 );
308 });
309
310 testWidgets('useNestedNavigation set to true enables nested navigation', (
311 WidgetTester tester,
312 ) async {
313 final GlobalKey scaffoldKey = GlobalKey();
314
315 Widget sheetScaffoldContent(BuildContext context) {
316 return Column(
317 children: <Widget>[
318 const Text('Page 2'),
319 CupertinoButton(
320 onPressed: () {
321 Navigator.push<void>(
322 context,
323 CupertinoPageRoute<void>(
324 builder: (BuildContext context) {
325 return CupertinoPageScaffold(
326 child: Column(
327 children: <Widget>[
328 const Text('Page 3'),
329 CupertinoButton(onPressed: () {}, child: const Text('Pop Page 3')),
330 ],
331 ),
332 );
333 },
334 ),
335 );
336 },
337 child: const Text('Push Page 3'),
338 ),
339 ],
340 );
341 }
342
343 await tester.pumpWidget(
344 CupertinoApp(
345 home: CupertinoPageScaffold(
346 key: scaffoldKey,
347 child: Center(
348 child: Column(
349 children: <Widget>[
350 const Text('Page 1'),
351 CupertinoButton(
352 onPressed: () {
353 showCupertinoSheet<void>(
354 context: scaffoldKey.currentContext!,
355 useNestedNavigation: true,
356 pageBuilder: (BuildContext context) {
357 return CupertinoPageScaffold(child: sheetScaffoldContent(context));
358 },
359 );
360 },
361 child: const Text('Push Page 2'),
362 ),
363 ],
364 ),
365 ),
366 ),
367 ),
368 );
369
370 expect(find.text('Page 1'), findsOneWidget);
371
372 await tester.tap(find.text('Push Page 2'));
373 await tester.pumpAndSettle();
374
375 expect(find.text('Page 2'), findsOneWidget);
376 expect(find.text('Page 3'), findsNothing);
377
378 final double pageTwoDY =
379 tester
380 .getTopLeft(
381 find.ancestor(of: find.text('Page 2'), matching: find.byType(CupertinoPageScaffold)),
382 )
383 .dy;
384 expect(pageTwoDY, greaterThan(0.0));
385
386 await tester.tap(find.text('Push Page 3'));
387 await tester.pumpAndSettle();
388
389 expect(find.text('Page 2'), findsNothing);
390 expect(find.text('Page 3'), findsOneWidget);
391
392 // New route should be at the same height as the previous route.
393 final double pageThreeDY =
394 tester
395 .getTopLeft(
396 find.ancestor(of: find.text('Page 3'), matching: find.byType(CupertinoPageScaffold)),
397 )
398 .dy;
399 expect(pageThreeDY, greaterThan(0.0));
400 expect(pageThreeDY, equals(pageTwoDY));
401 });
402
403 testWidgets('useNestedNavigation handles programmatic pops', (WidgetTester tester) async {
404 final GlobalKey scaffoldKey = GlobalKey();
405
406 Widget sheetScaffoldContent(BuildContext context) {
407 return Column(
408 children: <Widget>[
409 const Text('Page 2'),
410 CupertinoButton(
411 onPressed: () => Navigator.of(context).maybePop(),
412 child: const Text('Go Back'),
413 ),
414 ],
415 );
416 }
417
418 await tester.pumpWidget(
419 CupertinoApp(
420 home: CupertinoPageScaffold(
421 key: scaffoldKey,
422 child: Center(
423 child: Column(
424 children: <Widget>[
425 const Text('Page 1'),
426 CupertinoButton(
427 onPressed: () {
428 showCupertinoSheet<void>(
429 context: scaffoldKey.currentContext!,
430 useNestedNavigation: true,
431 pageBuilder: (BuildContext context) {
432 return CupertinoPageScaffold(child: sheetScaffoldContent(context));
433 },
434 );
435 },
436 child: const Text('Push Page 2'),
437 ),
438 ],
439 ),
440 ),
441 ),
442 ),
443 );
444
445 expect(find.text('Page 1'), findsOneWidget);
446 // The first page is at the top of the screen.
447 expect(
448 tester
449 .getTopLeft(
450 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
451 )
452 .dy,
453 equals(0.0),
454 );
455 expect(find.text('Page 2'), findsNothing);
456
457 await tester.tap(find.text('Push Page 2'));
458 await tester.pumpAndSettle();
459
460 expect(find.text('Page 2'), findsOneWidget);
461
462 // The first page, which is behind the top sheet but still partially visibile, is moved downwards.
463 expect(
464 tester
465 .getTopLeft(
466 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
467 )
468 .dy,
469 greaterThan(0.0),
470 );
471
472 await tester.tap(find.text('Go Back'));
473 await tester.pumpAndSettle();
474
475 // The first page would correctly transition back and sit at the top of the screen.
476 expect(find.text('Page 1'), findsOneWidget);
477 expect(
478 tester
479 .getTopLeft(
480 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
481 )
482 .dy,
483 equals(0.0),
484 );
485 expect(find.text('Page 2'), findsNothing);
486 });
487
488 testWidgets('useNestedNavigation handles system pop gestures', (WidgetTester tester) async {
489 final GlobalKey scaffoldKey = GlobalKey();
490
491 Widget sheetScaffoldContent(BuildContext context) {
492 return Column(
493 children: <Widget>[
494 const Text('Page 2'),
495 CupertinoButton(
496 onPressed: () {
497 Navigator.of(context).push(
498 CupertinoPageRoute<void>(
499 builder: (BuildContext context) {
500 return CupertinoPageScaffold(
501 child: Column(
502 children: <Widget>[
503 const Text('Page 3'),
504 CupertinoButton(
505 onPressed: () {
506 Navigator.of(context).pop();
507 },
508 child: const Text('Go back'),
509 ),
510 ],
511 ),
512 );
513 },
514 ),
515 );
516 },
517 child: const Text('Push Page 3'),
518 ),
519 ],
520 );
521 }
522
523 await tester.pumpWidget(
524 CupertinoApp(
525 home: CupertinoPageScaffold(
526 key: scaffoldKey,
527 child: Center(
528 child: Column(
529 children: <Widget>[
530 const Text('Page 1'),
531 CupertinoButton(
532 onPressed: () {
533 showCupertinoSheet<void>(
534 context: scaffoldKey.currentContext!,
535 useNestedNavigation: true,
536 pageBuilder: (BuildContext context) {
537 return CupertinoPageScaffold(child: sheetScaffoldContent(context));
538 },
539 );
540 },
541 child: const Text('Push Page 2'),
542 ),
543 ],
544 ),
545 ),
546 ),
547 ),
548 );
549
550 expect(find.text('Page 1'), findsOneWidget);
551 // The first page is at the top of the screen.
552 expect(
553 tester
554 .getTopLeft(
555 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
556 )
557 .dy,
558 equals(0.0),
559 );
560 expect(find.text('Page 2'), findsNothing);
561 expect(find.text('Page 3'), findsNothing);
562
563 await tester.tap(find.text('Push Page 2'));
564 await tester.pumpAndSettle();
565
566 expect(find.text('Page 2'), findsOneWidget);
567 expect(find.text('Page 3'), findsNothing);
568
569 // The first page, which is behind the top sheet but still partially visibile, is moved downwards.
570 expect(
571 tester
572 .getTopLeft(
573 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
574 )
575 .dy,
576 greaterThan(0.0),
577 );
578
579 await tester.tap(find.text('Push Page 3'));
580 await tester.pumpAndSettle();
581
582 expect(find.text('Page 3'), findsOneWidget);
583
584 // Simulate a system back gesture.
585 await simulateSystemBack();
586 await tester.pumpAndSettle();
587
588 // Go back to the first page within the sheet.
589 expect(find.text('Page 2'), findsOneWidget);
590 expect(find.text('Page 3'), findsNothing);
591
592 // The first page is still stacked behind the sheet.
593 expect(
594 tester
595 .getTopLeft(
596 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
597 )
598 .dy,
599 greaterThan(0.0),
600 );
601
602 await simulateSystemBack();
603 await tester.pumpAndSettle();
604
605 // The first page would correctly transition back and sit at the top of the screen.
606 expect(find.text('Page 1'), findsOneWidget);
607 expect(
608 tester
609 .getTopLeft(
610 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
611 )
612 .dy,
613 equals(0.0),
614 );
615 expect(find.text('Page 2'), findsNothing);
616 });
617
618 testWidgets('sheet has route settings', (WidgetTester tester) async {
619 await tester.pumpWidget(
620 CupertinoApp(
621 initialRoute: '/',
622 onGenerateRoute: (RouteSettings settings) {
623 if (settings.name == '/') {
624 return PageRouteBuilder<void>(
625 pageBuilder: (
626 BuildContext context,
627 Animation<double> animation,
628 Animation<double> secondaryAnimation,
629 ) {
630 return CupertinoPageScaffold(
631 navigationBar: const CupertinoNavigationBar(middle: Text('Page 1')),
632 child: Container(),
633 );
634 },
635 );
636 }
637 return CupertinoSheetRoute<void>(
638 builder: (BuildContext context) {
639 return CupertinoPageScaffold(
640 navigationBar: CupertinoNavigationBar(middle: Text('Page: ${settings.name}')),
641 child: Container(),
642 );
643 },
644 );
645 },
646 ),
647 );
648
649 expect(find.text('Page 1'), findsOneWidget);
650 expect(find.text('Page 2'), findsNothing);
651
652 tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
653 await tester.pumpAndSettle();
654
655 expect(find.text('Page: /next'), findsOneWidget);
656 });
657
658 testWidgets('content does not go below the bottom of the screen', (WidgetTester tester) async {
659 final GlobalKey scaffoldKey = GlobalKey();
660
661 await tester.pumpWidget(
662 CupertinoApp(
663 home: CupertinoPageScaffold(
664 key: scaffoldKey,
665 child: Center(
666 child: Column(
667 children: <Widget>[
668 const Text('Page 1'),
669 CupertinoButton(
670 onPressed: () {
671 Navigator.push<void>(
672 scaffoldKey.currentContext!,
673 CupertinoSheetRoute<void>(
674 builder: (BuildContext context) {
675 return CupertinoPageScaffold(child: Container());
676 },
677 ),
678 );
679 },
680 child: const Text('Push Page 2'),
681 ),
682 ],
683 ),
684 ),
685 ),
686 ),
687 );
688
689 await tester.tap(find.text('Push Page 2'));
690 await tester.pumpAndSettle();
691
692 expect(tester.getSize(find.byType(Container)).height, 600.0 - (600.0 * _kTopGapRatio));
693 });
694
695 testWidgets('nested navbars remove MediaQuery top padding', (WidgetTester tester) async {
696 final GlobalKey scaffoldKey = GlobalKey();
697 final GlobalKey appBarKey = GlobalKey();
698 final GlobalKey sheetBarKey = GlobalKey();
699
700 await tester.pumpWidget(
701 CupertinoApp(
702 home: MediaQuery(
703 data: const MediaQueryData(padding: EdgeInsets.fromLTRB(0, 20, 0, 0)),
704 child: CupertinoPageScaffold(
705 key: scaffoldKey,
706 navigationBar: CupertinoNavigationBar(
707 key: appBarKey,
708 middle: const Text('Navbar'),
709 backgroundColor: const Color(0xFFF8F8F8),
710 ),
711 child: Center(
712 child: Column(
713 children: <Widget>[
714 const Text('Page 1'),
715 CupertinoButton(
716 onPressed: () {
717 Navigator.push<void>(
718 scaffoldKey.currentContext!,
719 CupertinoSheetRoute<void>(
720 builder: (BuildContext context) {
721 return CupertinoPageScaffold(
722 navigationBar: CupertinoNavigationBar(
723 key: sheetBarKey,
724 middle: const Text('Navbar'),
725 ),
726 child: Container(),
727 );
728 },
729 ),
730 );
731 },
732 child: const Text('Push Page 2'),
733 ),
734 ],
735 ),
736 ),
737 ),
738 ),
739 ),
740 );
741 final double homeNavBardHeight = tester.getSize(find.byKey(appBarKey)).height;
742
743 await tester.tap(find.text('Push Page 2'));
744 await tester.pumpAndSettle();
745
746 final double sheetNavBarHeight = tester.getSize(find.byKey(sheetBarKey)).height;
747
748 expect(sheetNavBarHeight, lessThan(homeNavBardHeight));
749 });
750
751 testWidgets('Previous route corner radius goes to same when sheet route is popped', (
752 WidgetTester tester,
753 ) async {
754 final GlobalKey scaffoldKey = GlobalKey();
755
756 await tester.pumpWidget(
757 CupertinoApp(
758 home: CupertinoPageScaffold(
759 key: scaffoldKey,
760 child: Column(
761 children: <Widget>[
762 const Text('Page 1'),
763 CupertinoButton(
764 onPressed: () {
765 Navigator.push<void>(
766 scaffoldKey.currentContext!,
767 CupertinoSheetRoute<void>(
768 builder: (BuildContext context) {
769 return CupertinoPageScaffold(
770 child: GestureDetector(
771 onTap: () => Navigator.pop(context),
772 child: const Icon(Icons.arrow_back_ios),
773 ),
774 );
775 },
776 ),
777 );
778 },
779 child: const Text('Push Page 2'),
780 ),
781 ],
782 ),
783 ),
784 ),
785 );
786
787 expect(find.text('Page 1'), findsOneWidget);
788 expect(
789 tester
790 .getTopLeft(
791 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
792 )
793 .dy,
794 equals(0.0),
795 );
796 expect(find.byType(Icon), findsNothing);
797
798 await tester.tap(find.text('Push Page 2'));
799 await tester.pumpAndSettle();
800
801 // Previous page is still visible behind the new sheet.
802 expect(find.text('Page 1'), findsOneWidget);
803 final Offset pageOneOffset = tester.getTopLeft(
804 find.ancestor(of: find.text('Page 1'), matching: find.byType(CupertinoPageScaffold)),
805 );
806 expect(pageOneOffset.dy, greaterThan(0.0));
807 expect(pageOneOffset.dx, greaterThan(0.0));
808 expect(find.byType(Icon), findsOneWidget);
809
810 // Pop Sheet Route
811 await tester.tap(find.byType(Icon));
812 await tester.pumpAndSettle();
813
814 final Finder clipRRectFinder = find.byType(ClipRRect);
815 expect(clipRRectFinder, findsNothing);
816 });
817
818 testWidgets('Sheet transition does not interfere after popping', (WidgetTester tester) async {
819 final GlobalKey homeKey = GlobalKey();
820 final GlobalKey sheetKey = GlobalKey();
821 final GlobalKey popupMenuButtonKey = GlobalKey();
822
823 await tester.pumpWidget(
824 CupertinoApp(
825 localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
826 GlobalMaterialLocalizations.delegate,
827 GlobalWidgetsLocalizations.delegate,
828 GlobalCupertinoLocalizations.delegate,
829 ],
830 home: CupertinoPageScaffold(
831 key: homeKey,
832 child: CupertinoListTile(
833 onTap: () {
834 showCupertinoSheet<void>(
835 context: homeKey.currentContext!,
836 pageBuilder: (BuildContext context) {
837 return CupertinoPageScaffold(
838 key: sheetKey,
839 child: const Center(child: Text('Page 2')),
840 );
841 },
842 );
843 },
844 title: const Text('ListItem 0'),
845 trailing: Material(
846 type: MaterialType.transparency,
847 child: PopupMenuButton<int>(
848 key: popupMenuButtonKey,
849 itemBuilder: (BuildContext context) {
850 return <PopupMenuEntry<int>>[
851 const PopupMenuItem<int>(child: Text('Item 0')),
852 const PopupMenuItem<int>(child: Text('Item 1')),
853 ];
854 },
855 ),
856 ),
857 ),
858 ),
859 ),
860 );
861
862 await tester.tap(find.text('ListItem 0'));
863 await tester.pumpAndSettle();
864
865 expect(find.text('Page 2'), findsOneWidget);
866
867 final TestGesture gesture = await tester.startGesture(const Offset(100, 200));
868 await gesture.moveBy(const Offset(0, 350));
869 await tester.pump();
870
871 await gesture.up();
872 await tester.pumpAndSettle();
873
874 expect(find.text('Page 2'), findsNothing);
875 expect(find.text('ListItem 0'), findsOneWidget);
876
877 await tester.tap(find.byKey(popupMenuButtonKey));
878 await tester.pumpAndSettle();
879
880 expect(find.text('Item 0'), findsOneWidget);
881 expect(tester.takeException(), isNull);
882 });
883
884 group('drag dismiss gesture', () {
885 Widget dragGestureApp(GlobalKey homeScaffoldKey, GlobalKey sheetScaffoldKey) {
886 return CupertinoApp(
887 home: CupertinoPageScaffold(
888 key: homeScaffoldKey,
889 child: Center(
890 child: Column(
891 children: <Widget>[
892 const Text('Page 1'),
893 CupertinoButton(
894 onPressed: () {
895 showCupertinoSheet<void>(
896 context: homeScaffoldKey.currentContext!,
897 pageBuilder: (BuildContext context) {
898 return CupertinoPageScaffold(
899 key: sheetScaffoldKey,
900 child: const Center(child: Text('Page 2')),
901 );
902 },
903 );
904 },
905 child: const Text('Push Page 2'),
906 ),
907 ],
908 ),
909 ),
910 ),
911 );
912 }
913
914 testWidgets('partial drag and drop does not pop the sheet', (WidgetTester tester) async {
915 final GlobalKey homeKey = GlobalKey();
916 final GlobalKey sheetKey = GlobalKey();
917
918 await tester.pumpWidget(dragGestureApp(homeKey, sheetKey));
919
920 await tester.tap(find.text('Push Page 2'));
921 await tester.pumpAndSettle();
922
923 expect(find.text('Page 2'), findsOneWidget);
924
925 RenderBox box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
926 final double initialPosition = box.localToGlobal(Offset.zero).dy;
927
928 final TestGesture gesture = await tester.startGesture(const Offset(100, 200));
929 // Partial drag down
930 await gesture.moveBy(const Offset(0, 200));
931 await tester.pump();
932
933 box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
934 final double middlePosition = box.localToGlobal(Offset.zero).dy;
935 expect(middlePosition, greaterThan(initialPosition));
936
937 // Release gesture. Sheet should not pop and slide back up.
938 await gesture.up();
939 await tester.pumpAndSettle();
940
941 expect(find.text('Page 2'), findsOneWidget);
942
943 box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
944 final double finalPosition = box.localToGlobal(Offset.zero).dy;
945
946 expect(finalPosition, lessThan(middlePosition));
947 expect(finalPosition, equals(initialPosition));
948 });
949
950 testWidgets('dropping the drag further down the page pops the sheet', (
951 WidgetTester tester,
952 ) async {
953 final GlobalKey homeKey = GlobalKey();
954 final GlobalKey sheetKey = GlobalKey();
955
956 await tester.pumpWidget(dragGestureApp(homeKey, sheetKey));
957
958 await tester.tap(find.text('Push Page 2'));
959 await tester.pumpAndSettle();
960
961 expect(find.text('Page 2'), findsOneWidget);
962
963 final TestGesture gesture = await tester.startGesture(const Offset(100, 200));
964 await gesture.moveBy(const Offset(0, 350));
965 await tester.pump();
966
967 await gesture.up();
968 await tester.pumpAndSettle();
969
970 expect(find.text('Page 2'), findsNothing);
971 });
972
973 testWidgets('dismissing with a drag pops all nested routes', (WidgetTester tester) async {
974 final GlobalKey homeKey = GlobalKey();
975 final GlobalKey sheetKey = GlobalKey();
976
977 Widget sheetScaffoldContent(BuildContext context) {
978 return Column(
979 children: <Widget>[
980 const Text('Page 2'),
981 CupertinoButton(
982 onPressed: () {
983 Navigator.of(context).push(
984 CupertinoPageRoute<void>(
985 builder: (BuildContext context) {
986 return const CupertinoPageScaffold(child: Center(child: Text('Page 3')));
987 },
988 ),
989 );
990 },
991 child: const Text('Push Page 3'),
992 ),
993 ],
994 );
995 }
996
997 await tester.pumpWidget(
998 CupertinoApp(
999 home: CupertinoPageScaffold(
1000 key: homeKey,
1001 child: Center(
1002 child: Column(
1003 children: <Widget>[
1004 const Text('Page 1'),
1005 CupertinoButton(
1006 onPressed: () {
1007 showCupertinoSheet<void>(
1008 context: homeKey.currentContext!,
1009 useNestedNavigation: true,
1010 pageBuilder: (BuildContext context) {
1011 return CupertinoPageScaffold(
1012 key: sheetKey,
1013 child: sheetScaffoldContent(context),
1014 );
1015 },
1016 );
1017 },
1018 child: const Text('Push Page 2'),
1019 ),
1020 ],
1021 ),
1022 ),
1023 ),
1024 ),
1025 );
1026
1027 await tester.tap(find.text('Push Page 2'));
1028 await tester.pumpAndSettle();
1029
1030 expect(find.text('Page 2'), findsOneWidget);
1031
1032 await tester.tap(find.text('Push Page 3'));
1033 await tester.pumpAndSettle();
1034
1035 expect(find.text('Page 3'), findsOneWidget);
1036
1037 final TestGesture gesture = await tester.startGesture(const Offset(100, 200));
1038 await gesture.moveBy(const Offset(0, 350));
1039 await tester.pump();
1040
1041 await gesture.up();
1042 await tester.pumpAndSettle();
1043
1044 expect(find.text('Page 2'), findsNothing);
1045 expect(find.text('Page 3'), findsNothing);
1046 });
1047
1048 testWidgets('Popping the sheet during drag should not crash', (WidgetTester tester) async {
1049 final GlobalKey homeKey = GlobalKey();
1050 final GlobalKey sheetKey = GlobalKey();
1051
1052 await tester.pumpWidget(dragGestureApp(homeKey, sheetKey));
1053
1054 await tester.tap(find.text('Push Page 2'));
1055 await tester.pumpAndSettle();
1056
1057 final TestGesture gesture = await tester.createGesture();
1058
1059 await gesture.down(const Offset(100, 200));
1060
1061 // Need 2 events to form a valid drag
1062 await tester.pump(const Duration(milliseconds: 100));
1063 await gesture.moveTo(const Offset(100, 300), timeStamp: const Duration(milliseconds: 100));
1064 await tester.pump(const Duration(milliseconds: 200));
1065 await gesture.moveTo(const Offset(100, 500), timeStamp: const Duration(milliseconds: 200));
1066
1067 Navigator.of(homeKey.currentContext!).pop();
1068
1069 await tester.pumpAndSettle();
1070
1071 expect(find.text('Page 1'), findsOneWidget);
1072 expect(find.text('Page 2'), findsNothing);
1073
1074 await gesture.up();
1075 await tester.pumpAndSettle();
1076
1077 expect(find.text('Page 1'), findsOneWidget);
1078 });
1079
1080 testWidgets('Sheet should not block nested scroll', (WidgetTester tester) async {
1081 final GlobalKey homeKey = GlobalKey();
1082
1083 Widget sheetScaffoldContent(BuildContext context) {
1084 return ListView(
1085 children: const <Widget>[
1086 Text('Top of Scroll'),
1087 SizedBox(width: double.infinity, height: 100),
1088 Text('Middle of Scroll'),
1089 SizedBox(width: double.infinity, height: 100),
1090 ],
1091 );
1092 }
1093
1094 await tester.pumpWidget(
1095 CupertinoApp(
1096 home: CupertinoPageScaffold(
1097 key: homeKey,
1098 child: Center(
1099 child: Column(
1100 children: <Widget>[
1101 const Text('Page 1'),
1102 CupertinoButton(
1103 onPressed: () {
1104 showCupertinoSheet<void>(
1105 context: homeKey.currentContext!,
1106 pageBuilder: (BuildContext context) {
1107 return CupertinoPageScaffold(child: sheetScaffoldContent(context));
1108 },
1109 );
1110 },
1111 child: const Text('Push Page 2'),
1112 ),
1113 ],
1114 ),
1115 ),
1116 ),
1117 ),
1118 );
1119
1120 await tester.tap(find.text('Push Page 2'));
1121 await tester.pumpAndSettle();
1122
1123 expect(find.text('Top of Scroll'), findsOneWidget);
1124 final double startPosition = tester.getTopLeft(find.text('Middle of Scroll')).dy;
1125
1126 final TestGesture gesture = await tester.createGesture();
1127
1128 await gesture.down(const Offset(100, 100));
1129
1130 // Need 2 events to form a valid drag.
1131 await tester.pump(const Duration(milliseconds: 100));
1132 await gesture.moveTo(const Offset(100, 80), timeStamp: const Duration(milliseconds: 100));
1133 await tester.pump(const Duration(milliseconds: 200));
1134 await gesture.moveTo(const Offset(100, 50), timeStamp: const Duration(milliseconds: 200));
1135
1136 await tester.pumpAndSettle();
1137
1138 final double endPosition = tester.getTopLeft(find.text('Middle of Scroll')).dy;
1139
1140 // Final position should be higher.
1141 expect(endPosition, lessThan(startPosition));
1142
1143 await gesture.up();
1144 await tester.pumpAndSettle();
1145 });
1146
1147 testWidgets('dragging does not move the sheet when enableDrag is false', (
1148 WidgetTester tester,
1149 ) async {
1150 Widget nonDragGestureApp(GlobalKey homeScaffoldKey, GlobalKey sheetScaffoldKey) {
1151 return CupertinoApp(
1152 home: CupertinoPageScaffold(
1153 key: homeScaffoldKey,
1154 child: Center(
1155 child: Column(
1156 children: <Widget>[
1157 const Text('Page 1'),
1158 CupertinoButton(
1159 onPressed: () {
1160 showCupertinoSheet<void>(
1161 context: homeScaffoldKey.currentContext!,
1162 pageBuilder: (BuildContext context) {
1163 return CupertinoPageScaffold(
1164 key: sheetScaffoldKey,
1165 child: const Center(child: Text('Page 2')),
1166 );
1167 },
1168 enableDrag: false,
1169 );
1170 },
1171 child: const Text('Push Page 2'),
1172 ),
1173 ],
1174 ),
1175 ),
1176 ),
1177 );
1178 }
1179
1180 final GlobalKey homeKey = GlobalKey();
1181 final GlobalKey sheetKey = GlobalKey();
1182
1183 await tester.pumpWidget(nonDragGestureApp(homeKey, sheetKey));
1184
1185 await tester.tap(find.text('Push Page 2'));
1186 await tester.pumpAndSettle();
1187
1188 expect(find.text('Page 2'), findsOneWidget);
1189
1190 RenderBox box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
1191 final double initialPosition = box.localToGlobal(Offset.zero).dy;
1192
1193 final TestGesture gesture = await tester.startGesture(const Offset(100, 200));
1194 // Partial drag down
1195 await gesture.moveBy(const Offset(0, 200));
1196 await tester.pump();
1197
1198 // Release gesture. Sheet should not move.
1199 box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
1200 final double middlePosition = box.localToGlobal(Offset.zero).dy;
1201
1202 expect(middlePosition, equals(initialPosition));
1203
1204 await gesture.up();
1205 await tester.pumpAndSettle();
1206
1207 expect(find.text('Page 2'), findsOneWidget);
1208
1209 box = tester.renderObject(find.byKey(sheetKey)) as RenderBox;
1210 final double finalPosition = box.localToGlobal(Offset.zero).dy;
1211
1212 expect(finalPosition, equals(middlePosition));
1213 expect(finalPosition, equals(initialPosition));
1214 });
1215 });
1216
1217 testWidgets('CupertinoSheetTransition handles SystemUiOverlayStyle changes', (
1218 WidgetTester tester,
1219 ) async {
1220 final AnimationController controller = AnimationController(
1221 duration: const Duration(milliseconds: 100),
1222 vsync: const TestVSync(),
1223 );
1224 addTearDown(controller.dispose);
1225
1226 final Animation<double> secondaryAnimation = Tween<double>(
1227 begin: 1,
1228 end: 0,
1229 ).animate(controller);
1230
1231 await tester.pumpWidget(
1232 CupertinoApp(
1233 home: AnimatedBuilder(
1234 animation: controller,
1235 builder: (BuildContext context, Widget? child) {
1236 return CupertinoSheetTransition(
1237 primaryRouteAnimation: controller,
1238 secondaryRouteAnimation: secondaryAnimation,
1239 linearTransition: false,
1240 child: const SizedBox(),
1241 );
1242 },
1243 ),
1244 ),
1245 );
1246
1247 expect(SystemChrome.latestStyle!.statusBarBrightness, Brightness.dark);
1248 expect(SystemChrome.latestStyle!.statusBarIconBrightness, Brightness.light);
1249 });
1250}
1251

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com