Allow WIllPopCallback to return null or false to veto the pop. (#54640)

This commit is contained in:
Darren Austin 2020-04-13 12:35:03 -07:00 committed by GitHub
parent d1e052815e
commit ff2358623b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 4 deletions

View file

@ -1239,9 +1239,11 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
final List<WillPopCallback> _willPopCallbacks = <WillPopCallback>[];
/// Returns the value of the first callback added with
/// [addScopedWillPopCallback] that returns false. If they all return true,
/// returns the inherited method's result (see [Route.willPop]).
/// Returns [RoutePopDisposition.doNotPop] if any of callbacks added with
/// [addScopedWillPopCallback] returns either false or null. If they all
/// return true, the base [Route.willPop]'s result will be returned. The
/// callbacks will be called in the order they were added, and will only be
/// called if all previous callbacks returned true.
///
/// Typically this method is not overridden because applications usually
/// don't create modal routes directly, they use higher level primitives
@ -1260,7 +1262,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
final _ModalScopeState<T> scope = _scopeKey.currentState;
assert(scope != null);
for (final WillPopCallback callback in List<WillPopCallback>.from(_willPopCallbacks)) {
if (!await callback())
if (await callback() != true)
return RoutePopDisposition.doNotPop;
}
return await super.willPop();

View file

@ -128,6 +128,51 @@ void main() {
expect(find.text('Sample Page'), findsNothing);
});
testWidgets('willPop will only pop if the callback returns true', (WidgetTester tester) async {
Widget buildFrame() {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Home')),
body: Builder(
builder: (BuildContext context) {
return Center(
child: FlatButton(
child: const Text('X'),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) {
return SampleForm(
callback: () => Future<bool>.value(willPopValue),
);
},
));
},
),
);
},
),
),
);
}
await tester.pumpWidget(buildFrame());
await tester.tap(find.text('X'));
await tester.pumpAndSettle();
expect(find.text('Sample Form'), findsOneWidget);
// Should not pop if callback returns null
willPopValue = null;
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(find.text('Sample Form'), findsOneWidget);
// Should pop if callback returns true
willPopValue = true;
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
expect(find.text('Sample Form'), findsNothing);
});
testWidgets('Form.willPop can inhibit back button', (WidgetTester tester) async {
Widget buildFrame() {
return MaterialApp(