| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // 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'; |
| |
| class TestOverlayRoute extends OverlayRoute<void> { |
| TestOverlayRoute({ RouteSettings? settings }) : super(settings: settings); |
| @override |
| Iterable<OverlayEntry> createOverlayEntries() sync* { |
| yield OverlayEntry(builder: _build); |
| } |
| Widget _build(BuildContext context) => const Text('Overlay'); |
| } |
| |
| class PersistentBottomSheetTest extends StatefulWidget { |
| const PersistentBottomSheetTest({ Key? key }) : super(key: key); |
| |
| @override |
| PersistentBottomSheetTestState createState() => PersistentBottomSheetTestState(); |
| } |
| |
| class PersistentBottomSheetTestState extends State<PersistentBottomSheetTest> { |
| final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); |
| |
| bool setStateCalled = false; |
| |
| void showBottomSheet() { |
| _scaffoldKey.currentState!.showBottomSheet<void>((BuildContext context) { |
| return const Text('bottomSheet'); |
| }) |
| .closed.whenComplete(() { |
| setState(() { |
| setStateCalled = true; |
| }); |
| }); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return Scaffold( |
| key: _scaffoldKey, |
| body: const Text('Sheet'), |
| ); |
| } |
| } |
| |
| void main() { |
| testWidgets('Check onstage/offstage handling around transitions', (WidgetTester tester) async { |
| final GlobalKey containerKey1 = GlobalKey(); |
| final GlobalKey containerKey2 = GlobalKey(); |
| final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ |
| '/': (_) => Container(key: containerKey1, child: const Text('Home')), |
| '/settings': (_) => Container(key: containerKey2, child: const Text('Settings')), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), findsNothing); |
| expect(find.text('Overlay'), findsNothing); |
| |
| expect(Navigator.canPop(containerKey1.currentContext!), isFalse); |
| Navigator.pushNamed(containerKey1.currentContext!, '/settings'); |
| expect(Navigator.canPop(containerKey1.currentContext!), isTrue); |
| |
| await tester.pump(); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings', skipOffstage: false), isOffstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| await tester.pump(const Duration(milliseconds: 16)); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| Navigator.push(containerKey2.currentContext!, TestOverlayRoute()); |
| |
| await tester.pump(); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), isOnstage); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), isOnstage); |
| |
| expect(Navigator.canPop(containerKey2.currentContext!), isTrue); |
| Navigator.pop(containerKey2.currentContext!); |
| await tester.pump(); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| expect(Navigator.canPop(containerKey2.currentContext!), isTrue); |
| Navigator.pop(containerKey2.currentContext!); |
| await tester.pump(); |
| await tester.pump(); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), isOnstage); |
| expect(find.text('Overlay'), findsNothing); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), findsNothing); |
| expect(find.text('Overlay'), findsNothing); |
| |
| expect(Navigator.canPop(containerKey1.currentContext!), isFalse); |
| }); |
| |
| testWidgets('Check back gesture disables Heroes', (WidgetTester tester) async { |
| final GlobalKey containerKey1 = GlobalKey(); |
| final GlobalKey containerKey2 = GlobalKey(); |
| const String kHeroTag = 'hero'; |
| final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ |
| '/': (_) => Scaffold( |
| key: containerKey1, |
| body: Container( |
| color: const Color(0xff00ffff), |
| child: const Hero( |
| tag: kHeroTag, |
| child: Text('Home'), |
| ), |
| ), |
| ), |
| '/settings': (_) => Scaffold( |
| key: containerKey2, |
| body: Container( |
| padding: const EdgeInsets.all(100.0), |
| color: const Color(0xffff00ff), |
| child: const Hero( |
| tag: kHeroTag, |
| child: Text('Settings'), |
| ), |
| ), |
| ), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| Navigator.pushNamed(containerKey1.currentContext!, '/settings'); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 16)); |
| |
| expect(find.text('Settings'), isOnstage); |
| |
| // Settings text is heroing to its new location |
| Offset settingsOffset = tester.getTopLeft(find.text('Settings')); |
| expect(settingsOffset.dx, greaterThan(0.0)); |
| expect(settingsOffset.dx, lessThan(100.0)); |
| expect(settingsOffset.dy, greaterThan(0.0)); |
| expect(settingsOffset.dy, lessThan(100.0)); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| |
| // Drag from left edge to invoke the gesture. |
| final TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0)); |
| await gesture.moveBy(const Offset(50.0, 0.0)); |
| await tester.pump(); |
| |
| // Home is now visible. |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), isOnstage); |
| |
| // Home page is sliding in from the left, no heroes. |
| final Offset homeOffset = tester.getTopLeft(find.text('Home')); |
| expect(homeOffset.dx, lessThan(0.0)); |
| expect(homeOffset.dy, 0.0); |
| |
| // Settings page is sliding off to the right, no heroes. |
| settingsOffset = tester.getTopLeft(find.text('Settings')); |
| expect(settingsOffset.dx, greaterThan(100.0)); |
| expect(settingsOffset.dy, 100.0); |
| }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); |
| |
| testWidgets("Check back gesture doesn't start during transitions", (WidgetTester tester) async { |
| final GlobalKey containerKey1 = GlobalKey(); |
| final GlobalKey containerKey2 = GlobalKey(); |
| final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ |
| '/': (_) => Scaffold(key: containerKey1, body: const Text('Home')), |
| '/settings': (_) => Scaffold(key: containerKey2, body: const Text('Settings')), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| Navigator.pushNamed(containerKey1.currentContext!, '/settings'); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 100)); |
| |
| // We are mid-transition, both pages are on stage. |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), isOnstage); |
| |
| // Drag from left edge to invoke the gesture. (near bottom so we grab |
| // the Settings page as it comes up). |
| TestGesture gesture = await tester.startGesture(const Offset(5.0, 550.0)); |
| await gesture.moveBy(const Offset(500.0, 0.0)); |
| await gesture.up(); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 1000)); |
| |
| // The original forward navigation should have completed, instead of the |
| // back gesture, since we were mid transition. |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Settings'), isOnstage); |
| |
| // Try again now that we're settled. |
| gesture = await tester.startGesture(const Offset(5.0, 550.0)); |
| await gesture.moveBy(const Offset(500.0, 0.0)); |
| await gesture.up(); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 1000)); |
| |
| expect(find.text('Home'), isOnstage); |
| expect(find.text('Settings'), findsNothing); |
| }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); |
| |
| // Tests bug https://github.com/flutter/flutter/issues/6451 |
| testWidgets('Check back gesture with a persistent bottom sheet showing', (WidgetTester tester) async { |
| final GlobalKey containerKey1 = GlobalKey(); |
| final GlobalKey containerKey2 = GlobalKey(); |
| final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ |
| '/': (_) => Scaffold(key: containerKey1, body: const Text('Home')), |
| '/sheet': (_) => PersistentBottomSheetTest(key: containerKey2), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| Navigator.pushNamed(containerKey1.currentContext!, '/sheet'); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Sheet'), isOnstage); |
| |
| // Drag from left edge to invoke the gesture. We should go back. |
| TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0)); |
| await gesture.moveBy(const Offset(500.0, 0.0)); |
| await gesture.up(); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 1)); |
| |
| Navigator.pushNamed(containerKey1.currentContext!, '/sheet'); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Sheet'), isOnstage); |
| |
| // Show the bottom sheet. |
| final PersistentBottomSheetTestState sheet = containerKey2.currentState! as PersistentBottomSheetTestState; |
| sheet.showBottomSheet(); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| // Drag from left edge to invoke the gesture. Nothing should happen. |
| gesture = await tester.startGesture(const Offset(5.0, 100.0)); |
| await gesture.moveBy(const Offset(500.0, 0.0)); |
| await gesture.up(); |
| await tester.pump(); |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(find.text('Home'), findsNothing); |
| expect(find.text('Sheet'), isOnstage); |
| |
| // Sheet did not call setState (since the gesture did nothing). |
| expect(sheet.setStateCalled, isFalse); |
| }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); |
| |
| testWidgets('Test completed future', (WidgetTester tester) async { |
| final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{ |
| '/': (_) => const Center(child: Text('home')), |
| '/next': (_) => const Center(child: Text('next')), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| final PageRoute<void> route = MaterialPageRoute<void>( |
| settings: const RouteSettings(name: '/page'), |
| builder: (BuildContext context) => const Center(child: Text('page')), |
| ); |
| |
| int popCount = 0; |
| route.popped.whenComplete(() { |
| popCount += 1; |
| }); |
| |
| int completeCount = 0; |
| route.completed.whenComplete(() { |
| completeCount += 1; |
| }); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| Navigator.push(tester.element(find.text('home')), route); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| await tester.pump(); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(milliseconds: 100)); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(milliseconds: 100)); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| Navigator.pop(tester.element(find.text('page'))); |
| |
| expect(popCount, 0); |
| expect(completeCount, 0); |
| |
| await tester.pump(); |
| |
| expect(popCount, 1); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(milliseconds: 100)); |
| |
| expect(popCount, 1); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(milliseconds: 100)); |
| |
| expect(popCount, 1); |
| expect(completeCount, 0); |
| |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(popCount, 1); |
| expect(completeCount, 1); |
| }); |
| } |