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

diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index 585fd89..3dd804e 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -1239,9 +1239,11 @@
 
   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 @@
     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();
diff --git a/packages/flutter/test/material/will_pop_test.dart b/packages/flutter/test/material/will_pop_test.dart
index f41df05..de08545 100644
--- a/packages/flutter/test/material/will_pop_test.dart
+++ b/packages/flutter/test/material/will_pop_test.dart
@@ -128,6 +128,51 @@
     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(