Skip to content

Commit 204a884

Browse files
authored
PopScope example improvements (#142163)
Attempting to help users understand how to build a confirmation dialog when exiting a route.
1 parent e985c29 commit 204a884

3 files changed

Lines changed: 43 additions & 27 deletions

File tree

examples/api/lib/widgets/form/form.1.dart

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ class _SaveableFormState extends State<_SaveableForm> {
6262
});
6363
}
6464

65-
Future<void> _showDialog() async {
66-
final bool? shouldDiscard = await showDialog<bool>(
65+
/// Shows a dialog and resolves to true when the user has indicated that they
66+
/// want to pop.
67+
///
68+
/// A return value of null indicates a desire not to pop, such as when the
69+
/// user has dismissed the modal without tapping a button.
70+
Future<bool?> _showDialog() {
71+
return showDialog<bool>(
6772
context: context,
6873
builder: (BuildContext context) {
6974
return AlertDialog(
@@ -86,19 +91,13 @@ class _SaveableFormState extends State<_SaveableForm> {
8691
);
8792
},
8893
);
89-
90-
if (shouldDiscard ?? false) {
91-
// Since this is the root route, quit the app where possible by invoking
92-
// the SystemNavigator. If this wasn't the root route, then
93-
// Navigator.maybePop could be used instead.
94-
// See https://github.com/flutter/flutter/issues/11490
95-
SystemNavigator.pop();
96-
}
9794
}
9895

9996
void _save(String? value) {
97+
final String nextSavedValue = value ?? '';
10098
setState(() {
101-
_savedValue = value ?? '';
99+
_savedValue = nextSavedValue;
100+
_isDirty = nextSavedValue != _controller.text;
102101
});
103102
}
104103

@@ -112,11 +111,18 @@ class _SaveableFormState extends State<_SaveableForm> {
112111
const SizedBox(height: 20.0),
113112
Form(
114113
canPop: !_isDirty,
115-
onPopInvoked: (bool didPop) {
114+
onPopInvoked: (bool didPop) async {
116115
if (didPop) {
117116
return;
118117
}
119-
_showDialog();
118+
final bool shouldPop = await _showDialog() ?? false;
119+
if (shouldPop) {
120+
// Since this is the root route, quit the app where possible by
121+
// invoking the SystemNavigator. If this wasn't the root route,
122+
// then Navigator.maybePop could be used instead.
123+
// See https://github.com/flutter/flutter/issues/11490
124+
SystemNavigator.pop();
125+
}
120126
},
121127
autovalidateMode: AutovalidateMode.always,
122128
child: Column(
@@ -146,9 +152,9 @@ class _SaveableFormState extends State<_SaveableForm> {
146152
),
147153
),
148154
TextButton(
149-
onPressed: () {
150-
if (_isDirty) {
151-
_showDialog();
155+
onPressed: () async {
156+
final bool shouldPop = !_isDirty || (await _showDialog() ?? false);
157+
if (!shouldPop) {
152158
return;
153159
}
154160
// Since this is the root route, quit the app where possible by

examples/api/lib/widgets/pop_scope/pop_scope.0.dart

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,13 @@ class _PageTwo extends StatefulWidget {
6262
}
6363

6464
class _PageTwoState extends State<_PageTwo> {
65-
void _showBackDialog() {
66-
showDialog<void>(
65+
/// Shows a dialog and resolves to true when the user has indicated that they
66+
/// want to pop.
67+
///
68+
/// A return value of null indicates a desire not to pop, such as when the
69+
/// user has dismissed the modal without tapping a button.
70+
Future<bool?> _showBackDialog() {
71+
return showDialog<bool>(
6772
context: context,
6873
builder: (BuildContext context) {
6974
return AlertDialog(
@@ -78,7 +83,7 @@ class _PageTwoState extends State<_PageTwo> {
7883
),
7984
child: const Text('Nevermind'),
8085
onPressed: () {
81-
Navigator.pop(context);
86+
Navigator.pop(context, false);
8287
},
8388
),
8489
TextButton(
@@ -87,8 +92,7 @@ class _PageTwoState extends State<_PageTwo> {
8792
),
8893
child: const Text('Leave'),
8994
onPressed: () {
90-
Navigator.pop(context);
91-
Navigator.pop(context);
95+
Navigator.pop(context, true);
9296
},
9397
),
9498
],
@@ -107,15 +111,21 @@ class _PageTwoState extends State<_PageTwo> {
107111
const Text('Page Two'),
108112
PopScope(
109113
canPop: false,
110-
onPopInvoked: (bool didPop) {
114+
onPopInvoked: (bool didPop) async {
111115
if (didPop) {
112116
return;
113117
}
114-
_showBackDialog();
118+
final bool shouldPop = await _showBackDialog() ?? false;
119+
if (context.mounted && shouldPop) {
120+
Navigator.pop(context);
121+
}
115122
},
116123
child: TextButton(
117-
onPressed: () {
118-
_showBackDialog();
124+
onPressed: () async {
125+
final bool shouldPop = await _showBackDialog() ?? false;
126+
if (context.mounted && shouldPop) {
127+
Navigator.pop(context);
128+
}
119129
},
120130
child: const Text('Go back'),
121131
),

packages/flutter/lib/src/widgets/pop_scope.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import 'routes.dart';
2626
/// [PopScope], in which case it will be `false`.
2727
///
2828
/// {@tool dartpad}
29-
/// This sample demonstrates how to use this widget to handle nested navigation
30-
/// in a bottom navigation bar.
29+
/// This sample demonstrates showing a confirmation dialog before navigating
30+
/// away from a page.
3131
///
3232
/// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.0.dart **
3333
/// {@end-tool}

0 commit comments

Comments
 (0)