forked from flutter/packages
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixes flutter/flutter#137458 Chagnes: 1. Navigator.pop will always pop page based route 2. add a onDidRemovePage callback to replace onPopPage 3. Page.canPop and Page.onPopInvoked mirrors the PopScope, but in Page class. migration guide flutter/website#10523
- Loading branch information
Showing
8 changed files
with
358 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// 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. | ||
|
||
// This sample demonstrates showing a confirmation dialog before navigating | ||
// away from a page. | ||
|
||
import 'package:flutter/material.dart'; | ||
|
||
void main() => runApp(const PageApiExampleApp()); | ||
|
||
class PageApiExampleApp extends StatefulWidget { | ||
const PageApiExampleApp({super.key}); | ||
|
||
@override | ||
State<PageApiExampleApp> createState() => _PageApiExampleAppState(); | ||
} | ||
|
||
class _PageApiExampleAppState extends State<PageApiExampleApp> { | ||
final RouterDelegate<Object> delegate = MyRouterDelegate(); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return MaterialApp.router( | ||
routerDelegate: delegate, | ||
); | ||
} | ||
} | ||
|
||
class MyRouterDelegate extends RouterDelegate<Object> with PopNavigatorRouterDelegateMixin<Object>, ChangeNotifier { | ||
// This example doesn't use RouteInformationProvider. | ||
@override | ||
Future<void> setNewRoutePath(Object configuration) async => throw UnimplementedError(); | ||
|
||
@override | ||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>(); | ||
|
||
static MyRouterDelegate of(BuildContext context) => Router.of(context).routerDelegate as MyRouterDelegate; | ||
|
||
bool get showDetailPage => _showDetailPage; | ||
bool _showDetailPage = false; | ||
set showDetailPage(bool value) { | ||
if (_showDetailPage == value) { | ||
return; | ||
} | ||
_showDetailPage = value; | ||
notifyListeners(); | ||
} | ||
|
||
Future<bool> _showConfirmDialog() async { | ||
return await showDialog<bool>( | ||
context: navigatorKey.currentContext!, | ||
barrierDismissible: false, | ||
builder: (BuildContext context) { | ||
return AlertDialog( | ||
title: const Text('Are you sure?'), | ||
actions: <Widget>[ | ||
TextButton( | ||
child: const Text('Cancel'), | ||
onPressed: () { | ||
Navigator.of(context).pop(false); | ||
}, | ||
), | ||
TextButton( | ||
child: const Text('Confirm'), | ||
onPressed: () { | ||
Navigator.of(context).pop(true); | ||
}, | ||
), | ||
], | ||
); | ||
}, | ||
) ?? false; | ||
} | ||
|
||
Future<void> _handlePopDetails(bool didPop, void result) async { | ||
if (didPop) { | ||
showDetailPage = false; | ||
return; | ||
} | ||
final bool confirmed = await _showConfirmDialog(); | ||
if (confirmed) { | ||
showDetailPage = false; | ||
} | ||
} | ||
|
||
List<Page<Object?>> _getPages() { | ||
return <Page<Object?>>[ | ||
const MaterialPage<void>(key: ValueKey<String>('home'), child: _HomePage()), | ||
if (showDetailPage) | ||
MaterialPage<void>( | ||
key: const ValueKey<String>('details'), | ||
child: const _DetailsPage(), | ||
canPop: false, | ||
onPopInvoked: _handlePopDetails, | ||
), | ||
]; | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Navigator( | ||
key: navigatorKey, | ||
pages: _getPages(), | ||
onDidRemovePage: (Page<Object?> page) { | ||
assert(page.key == const ValueKey<String>('details')); | ||
showDetailPage = false; | ||
}, | ||
); | ||
} | ||
} | ||
|
||
class _HomePage extends StatefulWidget { | ||
const _HomePage(); | ||
|
||
@override | ||
State<_HomePage> createState() => _HomePageState(); | ||
} | ||
|
||
class _HomePageState extends State<_HomePage> { | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar(title: const Text('Home')), | ||
body: Center( | ||
child: TextButton( | ||
onPressed: () { | ||
MyRouterDelegate.of(context).showDetailPage = true; | ||
}, | ||
child: const Text('Go to details'), | ||
), | ||
), | ||
); | ||
} | ||
} | ||
|
||
class _DetailsPage extends StatefulWidget { | ||
const _DetailsPage(); | ||
|
||
@override | ||
State<_DetailsPage> createState() => _DetailsPageState(); | ||
} | ||
|
||
class _DetailsPageState extends State<_DetailsPage> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar(title: const Text('Details')), | ||
body: Center( | ||
child: TextButton( | ||
onPressed: () { | ||
Navigator.of(context).maybePop(); | ||
}, | ||
child: const Text('Go back'), | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// 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_api_samples/widgets/page/page_can_pop.0.dart' as example; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
import '../navigator_utils.dart'; | ||
|
||
void main() { | ||
testWidgets('Can choose to stay on page', (WidgetTester tester) async { | ||
await tester.pumpWidget( | ||
const example.PageApiExampleApp(), | ||
); | ||
|
||
expect(find.text('Home'), findsOneWidget); | ||
|
||
await tester.tap(find.text('Go to details')); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Home'), findsNothing); | ||
expect(find.text('Details'), findsOneWidget); | ||
|
||
await simulateSystemBack(); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Are you sure?'), findsOneWidget); | ||
|
||
await tester.tap(find.text('Cancel')); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Home'), findsNothing); | ||
expect(find.text('Details'), findsOneWidget); | ||
}); | ||
|
||
testWidgets('Can choose to go back', (WidgetTester tester) async { | ||
await tester.pumpWidget( | ||
const example.PageApiExampleApp(), | ||
); | ||
|
||
expect(find.text('Home'), findsOneWidget); | ||
|
||
await tester.tap(find.text('Go to details')); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Home'), findsNothing); | ||
expect(find.text('Details'), findsOneWidget); | ||
|
||
await simulateSystemBack(); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Are you sure?'), findsOneWidget); | ||
|
||
await tester.tap(find.text('Confirm')); | ||
await tester.pumpAndSettle(); | ||
expect(find.text('Details'), findsNothing); | ||
expect(find.text('Home'), findsOneWidget); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.