Add generic type for result in PopScope (#139164)

Adds a generic type and pop result to popscope and its friend.

The use cases are to be able to capture the result when the pop is called.

migration guide: https://github.com/flutter/website/pull/9872
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
index a68c2a1..a063c7c 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
@@ -48,7 +48,7 @@
 
   @override
   Widget build(BuildContext context) {
-    return PopScope(
+    return PopScope<Object?>(
       // Prevent swipe popping of this page. Use explicit exit buttons only.
       canPop: false,
       child: DefaultTextStyle(
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
index 81ee4da..86c5773 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
@@ -110,7 +110,7 @@
   bool _hasName = false;
   late String _eventName;
 
-  Future<void> _handlePopInvoked(bool didPop) async {
+  Future<void> _handlePopInvoked(bool didPop, Object? result) async {
     if (didPop) {
       return;
     }
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
index c6f644e..dd479d5 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
@@ -143,7 +143,7 @@
     return null;
   }
 
-  Future<void> _handlePopInvoked(bool didPop) async {
+  Future<void> _handlePopInvoked(bool didPop, Object? result) async {
     if (didPop) {
       return;
     }
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart b/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart
index 5e7a951..1a90c14 100644
--- a/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart
+++ b/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart
@@ -355,7 +355,7 @@
 
   // Closes the cart if the cart is open, otherwise exits the app (this should
   // only be relevant for Android).
-  void _handlePopInvoked(bool didPop) {
+  void _handlePopInvoked(bool didPop, Object? result) {
     if (didPop) {
       return;
     }
@@ -370,7 +370,7 @@
       duration: const Duration(milliseconds: 225),
       curve: Curves.easeInOut,
       alignment: FractionalOffset.topLeft,
-      child: PopScope(
+      child: PopScope<Object?>(
         canPop: !_isOpen,
         onPopInvoked: _handlePopInvoked,
         child: AnimatedBuilder(
diff --git a/dev/integration_tests/flutter_gallery/lib/gallery/home.dart b/dev/integration_tests/flutter_gallery/lib/gallery/home.dart
index a6ceab8..1e57e39 100644
--- a/dev/integration_tests/flutter_gallery/lib/gallery/home.dart
+++ b/dev/integration_tests/flutter_gallery/lib/gallery/home.dart
@@ -326,9 +326,9 @@
       backgroundColor: isDark ? _kFlutterBlue : theme.primaryColor,
       body: SafeArea(
         bottom: false,
-        child: PopScope(
+        child: PopScope<Object?>(
           canPop: _category == null,
-          onPopInvoked: (bool didPop) {
+          onPopInvoked: (bool didPop, Object? result) {
             if (didPop) {
               return;
             }
diff --git a/examples/api/lib/widgets/form/form.1.dart b/examples/api/lib/widgets/form/form.1.dart
index e008f5a..c6d602d 100644
--- a/examples/api/lib/widgets/form/form.1.dart
+++ b/examples/api/lib/widgets/form/form.1.dart
@@ -111,7 +111,7 @@
           const SizedBox(height: 20.0),
           Form(
             canPop: !_isDirty,
-            onPopInvoked: (bool didPop) async {
+            onPopInvoked: (bool didPop, Object? result) async {
               if (didPop) {
                 return;
               }
diff --git a/examples/api/lib/widgets/pop_scope/pop_scope.0.dart b/examples/api/lib/widgets/pop_scope/pop_scope.0.dart
index 2400b09..c86640c 100644
--- a/examples/api/lib/widgets/pop_scope/pop_scope.0.dart
+++ b/examples/api/lib/widgets/pop_scope/pop_scope.0.dart
@@ -109,9 +109,9 @@
           mainAxisAlignment: MainAxisAlignment.center,
           children: <Widget>[
             const Text('Page Two'),
-            PopScope(
+            PopScope<Object?>(
               canPop: false,
-              onPopInvoked: (bool didPop) async {
+              onPopInvoked: (bool didPop, Object? result) async {
                 if (didPop) {
                   return;
                 }
diff --git a/examples/api/lib/widgets/pop_scope/pop_scope.1.dart b/examples/api/lib/widgets/pop_scope/pop_scope.1.dart
new file mode 100644
index 0000000..cb3b9f2
--- /dev/null
+++ b/examples/api/lib/widgets/pop_scope/pop_scope.1.dart
@@ -0,0 +1,232 @@
+// 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 how to use PopScope to wrap widget that
+// may pop the page with a result.
+
+import 'package:flutter/material.dart';
+
+void main() => runApp(const NavigatorPopHandlerApp());
+
+class NavigatorPopHandlerApp extends StatelessWidget {
+  const NavigatorPopHandlerApp({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      initialRoute: '/home',
+      onGenerateRoute: (RouteSettings settings) {
+        return switch (settings.name) {
+        '/two' => MaterialPageRoute<FormData>(
+          builder: (BuildContext context) => const _PageTwo(),
+        ),
+        _ => MaterialPageRoute<void>(
+        builder: (BuildContext context) => const _HomePage(),
+        ),
+        };
+      },
+    );
+  }
+}
+
+class _HomePage extends StatefulWidget {
+  const _HomePage();
+
+  @override
+  State<_HomePage> createState() => _HomePageState();
+}
+
+class _HomePageState extends State<_HomePage> {
+  FormData? _formData;
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            const Text('Page One'),
+            if (_formData != null)
+              Text('Hello ${_formData!.name}, whose favorite food is ${_formData!.favoriteFood}.'),
+            TextButton(
+              onPressed: () async {
+                final FormData formData =
+                    await Navigator.of(context).pushNamed<FormData?>('/two')
+                        ?? const FormData();
+                if (formData != _formData) {
+                  setState(() {
+                    _formData = formData;
+                  });
+                }
+              },
+              child: const Text('Next page'),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+class _PopScopeWrapper extends StatelessWidget {
+  const _PopScopeWrapper({required this.child});
+
+  final Widget child;
+
+  Future<bool?> _showBackDialog(BuildContext context) {
+    return showDialog<bool>(
+      context: context,
+      builder: (BuildContext context) {
+        return AlertDialog(
+          title: const Text('Are you sure?'),
+          content: const Text(
+            'Are you sure you want to leave this page?',
+          ),
+          actions: <Widget>[
+            TextButton(
+              style: TextButton.styleFrom(
+                textStyle: Theme.of(context).textTheme.labelLarge,
+              ),
+              child: const Text('Never mind'),
+              onPressed: () {
+                Navigator.pop(context, false);
+              },
+            ),
+            TextButton(
+              style: TextButton.styleFrom(
+                textStyle: Theme.of(context).textTheme.labelLarge,
+              ),
+              child: const Text('Leave'),
+              onPressed: () {
+                Navigator.pop(context, true);
+              },
+            ),
+          ],
+        );
+      },
+    );
+  }
+  @override
+  Widget build(BuildContext context) {
+    return PopScope<FormData>(
+      canPop: false,
+      // The result contains pop result in `_PageTwo`.
+      onPopInvoked: (bool didPop, FormData? result) async {
+        if (didPop) {
+          return;
+        }
+        final bool shouldPop = await _showBackDialog(context) ?? false;
+        if (context.mounted && shouldPop) {
+          Navigator.pop(context, result);
+        }
+      },
+      child: const _PageTwoBody(),
+    );
+  }
+}
+
+// This is a PopScope wrapper over _PageTwoBody
+class _PageTwo extends StatelessWidget {
+  const _PageTwo();
+
+  @override
+  Widget build(BuildContext context) {
+    return const _PopScopeWrapper(
+      child: _PageTwoBody(),
+    );
+  }
+
+}
+
+class _PageTwoBody extends StatefulWidget {
+  const _PageTwoBody();
+
+  @override
+  State<_PageTwoBody> createState() => _PageTwoBodyState();
+}
+
+class _PageTwoBodyState extends State<_PageTwoBody> {
+  FormData _formData = const FormData();
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            const Text('Page Two'),
+            Form(
+              child: Column(
+                children: <Widget>[
+                  TextFormField(
+                    decoration: const InputDecoration(
+                      hintText: 'Enter your name.',
+                    ),
+                    onChanged: (String value) {
+                      _formData = _formData.copyWith(
+                        name: value,
+                      );
+                    },
+                  ),
+                  TextFormField(
+                    decoration: const InputDecoration(
+                      hintText: 'Enter your favorite food.',
+                    ),
+                    onChanged: (String value) {
+                      _formData = _formData.copyWith(
+                        favoriteFood: value,
+                      );
+                    },
+                  ),
+                ],
+              ),
+            ),
+            TextButton(
+              onPressed: () async {
+                Navigator.maybePop(context, _formData);
+              },
+              child: const Text('Go back'),
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+@immutable
+class FormData {
+  const FormData({
+    this.name = '',
+    this.favoriteFood = '',
+  });
+
+  final String name;
+  final String favoriteFood;
+
+  FormData copyWith({String? name, String? favoriteFood}) {
+    return FormData(
+      name: name ?? this.name,
+      favoriteFood: favoriteFood ?? this.favoriteFood,
+    );
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) {
+      return true;
+    }
+    if (other.runtimeType != runtimeType) {
+      return false;
+    }
+    return other is FormData
+        && other.name == name
+        && other.favoriteFood == favoriteFood;
+  }
+
+  @override
+  int get hashCode => Object.hash(name, favoriteFood);
+}
diff --git a/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart b/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart
new file mode 100644
index 0000000..14266af
--- /dev/null
+++ b/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart
@@ -0,0 +1,67 @@
+// 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_api_samples/widgets/pop_scope/pop_scope.1.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.NavigatorPopHandlerApp(),
+    );
+
+    expect(find.text('Page One'), findsOneWidget);
+
+    await tester.tap(find.text('Next page'));
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsNothing);
+    expect(find.text('Page Two'), findsOneWidget);
+
+    await simulateSystemBack();
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsNothing);
+    expect(find.text('Page Two'), findsOneWidget);
+    expect(find.text('Are you sure?'), findsOneWidget);
+
+    await tester.tap(find.text('Never mind'));
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsNothing);
+    expect(find.text('Page Two'), findsOneWidget);
+  });
+
+  testWidgets('Can choose to go back with pop result', (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.NavigatorPopHandlerApp(),
+    );
+
+    expect(find.text('Page One'), findsOneWidget);
+    expect(find.text('Page Two'), findsNothing);
+
+    await tester.tap(find.text('Next page'));
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsNothing);
+    expect(find.text('Page Two'), findsOneWidget);
+
+    await tester.enterText(find.byType(TextFormField).first, 'John');
+    await tester.pumpAndSettle();
+    await tester.enterText(find.byType(TextFormField).last, 'Apple');
+    await tester.pumpAndSettle();
+
+    await tester.tap(find.text('Go back'));
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsNothing);
+    expect(find.text('Page Two'), findsOneWidget);
+    expect(find.text('Are you sure?'), findsOneWidget);
+
+    await tester.tap(find.text('Leave'));
+    await tester.pumpAndSettle();
+    expect(find.text('Page One'), findsOneWidget);
+    expect(find.text('Page Two'), findsNothing);
+    expect(find.text('Are you sure?'), findsNothing);
+    expect(find.text('Hello John, whose favorite food is Apple.'), findsOneWidget);
+  });
+}
diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart
index a61f3a0..308a9d8 100644
--- a/packages/flutter/lib/src/material/about.dart
+++ b/packages/flutter/lib/src/material/about.dart
@@ -1230,9 +1230,9 @@
   }
 
   MaterialPageRoute<void> _detailPageRoute(Object? arguments) {
-    return MaterialPageRoute<dynamic>(builder: (BuildContext context) {
-      return PopScope(
-        onPopInvoked: (bool didPop) {
+    return MaterialPageRoute<void>(builder: (BuildContext context) {
+      return PopScope<void>(
+        onPopInvoked: (bool didPop, void result) {
           // No need for setState() as rebuild happens on navigation pop.
           focus = _Focus.master;
         },
diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart
index 2211523..823235b 100644
--- a/packages/flutter/lib/src/widgets/form.dart
+++ b/packages/flutter/lib/src/widgets/form.dart
@@ -177,7 +177,7 @@
   ///  * [canPop], which also comes from [PopScope] and is often used in
   ///    conjunction with this parameter.
   ///  * [PopScope.onPopInvoked], which is what [Form] delegates to internally.
-  final PopInvokedCallback? onPopInvoked;
+  final PopInvokedCallback<Object?>? onPopInvoked;
 
   /// Called when one of the form fields changes.
   ///
@@ -244,7 +244,7 @@
     }
 
     if (widget.canPop != null || widget.onPopInvoked != null) {
-      return PopScope(
+      return PopScope<Object?>(
         canPop: widget.canPop ?? true,
         onPopInvoked: widget.onPopInvoked,
         child: _FormScope(
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart
index 88b869b..edf3eb1 100644
--- a/packages/flutter/lib/src/widgets/navigator.dart
+++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -355,7 +355,7 @@
   /// will still be called. The `didPop` parameter indicates whether or not the
   /// back navigation actually happened successfully.
   /// {@endtemplate}
-  void onPopInvoked(bool didPop) {}
+  void onPopInvoked(bool didPop, T? result) {}
 
   /// Whether calling [didPop] would return false.
   bool get willHandlePopInternally => false;
@@ -3109,7 +3109,7 @@
     assert(isPresent);
     pendingResult = result;
     currentState = _RouteLifecycle.pop;
-    route.onPopInvoked(true);
+    route.onPopInvoked(true, result);
   }
 
   bool _reportRemovalToObserver = true;
@@ -5239,7 +5239,7 @@
         pop(result);
         return true;
       case RoutePopDisposition.doNotPop:
-        lastEntry.route.onPopInvoked(false);
+        lastEntry.route.onPopInvoked(false, result);
         return true;
     }
   }
@@ -5282,7 +5282,7 @@
         assert(entry.route._popCompleter.isCompleted);
         entry.currentState = _RouteLifecycle.pop;
       }
-      entry.route.onPopInvoked(true);
+      entry.route.onPopInvoked(true, result);
     } else {
       entry.pop<T>(result);
       assert (entry.currentState == _RouteLifecycle.pop);
diff --git a/packages/flutter/lib/src/widgets/navigator_pop_handler.dart b/packages/flutter/lib/src/widgets/navigator_pop_handler.dart
index 203a85b..627a9cf 100644
--- a/packages/flutter/lib/src/widgets/navigator_pop_handler.dart
+++ b/packages/flutter/lib/src/widgets/navigator_pop_handler.dart
@@ -81,9 +81,9 @@
   Widget build(BuildContext context) {
     // When the widget subtree indicates it can handle a pop, disable popping
     // here, so that it can be manually handled in canPop.
-    return PopScope(
+    return PopScope<Object?>(
       canPop: !widget.enabled || _canPop,
-      onPopInvoked: (bool didPop) {
+      onPopInvoked: (bool didPop, Object? result) {
         if (didPop) {
           return;
         }
diff --git a/packages/flutter/lib/src/widgets/pop_scope.dart b/packages/flutter/lib/src/widgets/pop_scope.dart
index c8b31f6..675f701 100644
--- a/packages/flutter/lib/src/widgets/pop_scope.dart
+++ b/packages/flutter/lib/src/widgets/pop_scope.dart
@@ -10,10 +10,15 @@
 
 /// Manages back navigation gestures.
 ///
+/// The generic type should match or be supertype of the generic type of the
+/// enclosing [Route]. If the enclosing Route is a `MaterialPageRoute<int>`,
+/// you can define [PopScope] with int or any supertype of int.
+///
 /// The [canPop] parameter disables back gestures when set to `false`.
 ///
 /// The [onPopInvoked] parameter reports when pop navigation was attempted, and
-/// `didPop` indicates whether or not the navigation was successful.
+/// `didPop` indicates whether or not the navigation was successful. The
+/// `result` contains the pop result.
 ///
 /// Android has a system back gesture that is a swipe inward from near the edge
 /// of the screen. It is recognized by Android before being passed to Flutter.
@@ -41,6 +46,13 @@
 /// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.0.dart **
 /// {@end-tool}
 ///
+/// {@tool dartpad}
+/// This sample demonstrates showing how to use PopScope to wrap widget that
+/// may pop the page with a result.
+///
+/// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.1.dart **
+/// {@end-tool}
+///
 /// See also:
 ///
 ///  * [NavigatorPopHandler], which is a less verbose way to handle system back
@@ -49,7 +61,7 @@
 ///    back gestures in the case of a form with unsaved data.
 ///  * [ModalRoute.registerPopEntry] and [ModalRoute.unregisterPopEntry],
 ///    which this widget uses to integrate with Flutter's navigation system.
-class PopScope extends StatefulWidget {
+class PopScope<T> extends StatefulWidget {
   /// Creates a widget that registers a callback to veto attempts by the user to
   /// dismiss the enclosing [ModalRoute].
   const PopScope({
@@ -78,10 +90,12 @@
   /// indicates whether or not the back navigation actually happened
   /// successfully.
   ///
+  /// The `result` contains the pop result.
+  ///
   /// See also:
   ///
   ///  * [Route.onPopInvoked], which is similar.
-  final PopInvokedCallback? onPopInvoked;
+  final PopInvokedCallback<T>? onPopInvoked;
 
   /// {@template flutter.widgets.PopScope.canPop}
   /// When false, blocks the current route from being popped.
@@ -99,14 +113,16 @@
   final bool canPop;
 
   @override
-  State<PopScope> createState() => _PopScopeState();
+  State<PopScope<T>> createState() => _PopScopeState<T>();
 }
 
-class _PopScopeState extends State<PopScope> implements PopEntry {
+class _PopScopeState<T> extends State<PopScope<T>> implements PopEntry<T> {
   ModalRoute<dynamic>? _route;
 
   @override
-  PopInvokedCallback? get onPopInvoked => widget.onPopInvoked;
+  void onPopInvoked(bool didPop, T? result) {
+    widget.onPopInvoked?.call(didPop, result);
+  }
 
   @override
   late final ValueNotifier<bool> canPopNotifier;
@@ -129,7 +145,7 @@
   }
 
   @override
-  void didUpdateWidget(PopScope oldWidget) {
+  void didUpdateWidget(PopScope<T> oldWidget) {
     super.didUpdateWidget(oldWidget);
     canPopNotifier.value = widget.canPop;
   }
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart
index 277a5fb..eb1976c 100644
--- a/packages/flutter/lib/src/widgets/routes.dart
+++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -1669,7 +1669,9 @@
 
   final List<WillPopCallback> _willPopCallbacks = <WillPopCallback>[];
 
-  final Set<PopEntry> _popEntries = <PopEntry>{};
+  // Holding as Object? instead of T so that PopScope in this route can be
+  // declared with any supertype of T.
+  final Set<PopEntry<Object?>> _popEntries = <PopEntry<Object?>>{};
 
   /// Returns [RoutePopDisposition.doNotPop] if any of callbacks added with
   /// [addScopedWillPopCallback] returns either false or null. If they all
@@ -1724,7 +1726,7 @@
   ///    method checks.
   @override
   RoutePopDisposition get popDisposition {
-    for (final PopEntry popEntry in _popEntries) {
+    for (final PopEntry<Object?> popEntry in _popEntries) {
       if (!popEntry.canPopNotifier.value) {
         return RoutePopDisposition.doNotPop;
       }
@@ -1734,9 +1736,9 @@
   }
 
   @override
-  void onPopInvoked(bool didPop) {
-    for (final PopEntry popEntry in _popEntries) {
-      popEntry.onPopInvoked?.call(didPop);
+  void onPopInvoked(bool didPop, T? result) {
+    for (final PopEntry<Object?> popEntry in _popEntries) {
+      popEntry.onPopInvoked(didPop, result);
     }
   }
 
@@ -1793,7 +1795,7 @@
   /// See also:
   ///
   ///  * [unregisterPopEntry], which performs the opposite operation.
-  void registerPopEntry(PopEntry popEntry) {
+  void registerPopEntry(PopEntry<Object?> popEntry) {
     _popEntries.add(popEntry);
     popEntry.canPopNotifier.addListener(_handlePopEntryChange);
     _handlePopEntryChange();
@@ -1804,7 +1806,7 @@
   /// See also:
   ///
   ///  * [registerPopEntry], which performs the opposite operation.
-  void unregisterPopEntry(PopEntry popEntry) {
+  void unregisterPopEntry(PopEntry<Object?> popEntry) {
     _popEntries.remove(popEntry);
     popEntry.canPopNotifier.removeListener(_handlePopEntryChange);
     _handlePopEntryChange();
@@ -2413,7 +2415,9 @@
 ///
 /// Accepts a didPop boolean indicating whether or not back navigation
 /// succeeded.
-typedef PopInvokedCallback = void Function(bool didPop);
+///
+/// The `result` contains the pop result.
+typedef PopInvokedCallback<T> = void Function(bool didPop, T? result);
 
 /// Allows listening to and preventing pops.
 ///
@@ -2425,9 +2429,13 @@
 ///  * [PopScope], which provides similar functionality in a widget.
 ///  * [ModalRoute.registerPopEntry], which unregisters instances of this.
 ///  * [ModalRoute.unregisterPopEntry], which unregisters instances of this.
-abstract class PopEntry {
+abstract class PopEntry<T> {
   /// {@macro flutter.widgets.PopScope.onPopInvoked}
-  PopInvokedCallback? get onPopInvoked;
+  // This can't be a function getter since dart vm doesn't allow upcasting
+  // generic type of the function getter. This prevents customers from declaring
+  // PopScope with any generic type that is subtype of ModalRoute._popEntries.
+  // See https://github.com/dart-lang/sdk/issues/55427.
+  void onPopInvoked(bool didPop, T? result);
 
   /// {@macro flutter.widgets.PopScope.canPop}
   ValueListenable<bool> get canPopNotifier;
diff --git a/packages/flutter/test/cupertino/tab_test.dart b/packages/flutter/test/cupertino/tab_test.dart
index 5acf173..71887775 100644
--- a/packages/flutter/test/cupertino/tab_test.dart
+++ b/packages/flutter/test/cupertino/tab_test.dart
@@ -305,7 +305,7 @@
               BottomNavigationBarItem(label: '', icon: Text('2'))
             ],
           ),
-          tabBuilder: (_, int i) => PopScope(
+          tabBuilder: (_, int i) => PopScope<Object?>(
             canPop: false,
             child: CupertinoTabView(
               navigatorKey: key,
diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart
index 0bb9ba9..4cc9a7f 100644
--- a/packages/flutter/test/widgets/navigator_test.dart
+++ b/packages/flutter/test/widgets/navigator_test.dart
@@ -2989,7 +2989,7 @@
       const List<Page<void>> myPages = <Page<void>>[
         MaterialPage<void>(child: Text('page1')),
         MaterialPage<void>(
-          child: PopScope(
+          child: PopScope<void>(
             canPop: false,
             child: Text('page2'),
           ),
@@ -4908,9 +4908,9 @@
             home: StatefulBuilder(
               builder: (BuildContext context, StateSetter setState) {
                 builderSetState = setState;
-                return PopScope(
+                return PopScope<Object?>(
                   canPop: canPop(),
-                  onPopInvoked: (bool success) {
+                  onPopInvoked: (bool success, Object? result) {
                     if (success || pages.last == _Page.noPop) {
                       return;
                     }
@@ -5024,9 +5024,9 @@
           MaterialApp(
             home: StatefulBuilder(
               builder: (BuildContext context, StateSetter setState) {
-                return PopScope(
+                return PopScope<Object?>(
                   canPop: canPop(),
-                  onPopInvoked: (bool success) {
+                  onPopInvoked: (bool success, Object? result) {
                     if (success || pages.last == _Page.noPop) {
                       return;
                     }
@@ -5117,9 +5117,9 @@
           MaterialApp(
             home: StatefulBuilder(
               builder: (BuildContext context, StateSetter setState) {
-                return PopScope(
+                return PopScope<Object?>(
                   canPop: canPop(),
-                  onPopInvoked: (bool success) {
+                  onPopInvoked: (bool success, Object? result) {
                     if (success || pages.last == _PageWithYesPop.noPop) {
                       return;
                     }
@@ -5189,7 +5189,7 @@
                             child: _LinksPage(
                               title: 'Can pop page',
                               canPop: true,
-                              onPopInvoked: (bool didPop) {
+                              onPopInvoked: (bool didPop, void result) {
                                 onPopInvokedCallCount += 1;
                               },
                             ),
@@ -5556,7 +5556,7 @@
   final bool? canPop;
   final VoidCallback? onBack;
   final String title;
-  final PopInvokedCallback? onPopInvoked;
+  final PopInvokedCallback<Object?>? onPopInvoked;
 
   @override
   Widget build(BuildContext context) {
@@ -5575,7 +5575,7 @@
                 child: const Text('Go back'),
               ),
             if (canPop != null)
-              PopScope(
+              PopScope<void>(
                 canPop: canPop!,
                 onPopInvoked: onPopInvoked,
                 child: const SizedBox.shrink(),
diff --git a/packages/flutter/test/widgets/pop_scope_test.dart b/packages/flutter/test/widgets/pop_scope_test.dart
index 116951c..7da2019 100644
--- a/packages/flutter/test/widgets/pop_scope_test.dart
+++ b/packages/flutter/test/widgets/pop_scope_test.dart
@@ -47,7 +47,7 @@
               builder: (BuildContext buildContext, StateSetter stateSetter) {
                 context = buildContext;
                 setState = stateSetter;
-                return PopScope(
+                return PopScope<Object?>(
                   canPop: canPop,
                   child: const Center(
                     child: Column(
@@ -79,6 +79,94 @@
     variant: TargetPlatformVariant.all(),
   );
 
+  testWidgets('pop scope can receive result', (WidgetTester tester) async {
+    Object? receivedResult;
+    final Object poppedResult = Object();
+    final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
+    await tester.pumpWidget(
+      MaterialApp(
+        initialRoute: '/',
+        navigatorKey: nav,
+        home: Scaffold(
+          body: PopScope<Object?>(
+            canPop: false,
+            onPopInvoked: (bool didPop, Object? result) {
+              receivedResult = result;
+            },
+            child: const Center(
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: <Widget>[
+                  Text('Home/PopScope Page'),
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+
+    nav.currentState!.maybePop(poppedResult);
+    await tester.pumpAndSettle();
+    expect(receivedResult, poppedResult);
+  },
+    variant: TargetPlatformVariant.all(),
+  );
+
+  testWidgets('pop scope can have Object? generic type while route has stricter generic type', (WidgetTester tester) async {
+    Object? receivedResult;
+    const int poppedResult = 13;
+    final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
+    await tester.pumpWidget(
+      MaterialApp(
+        initialRoute: '/',
+        navigatorKey: nav,
+        home: Scaffold(
+          body: PopScope<Object?>(
+            canPop: false,
+            onPopInvoked: (bool didPop, Object? result) {
+              receivedResult = result;
+            },
+            child: const Center(
+              child: Column(
+                mainAxisAlignment: MainAxisAlignment.center,
+                children: <Widget>[
+                  Text('Home/PopScope Page'),
+                ],
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+
+    nav.currentState!.push(
+      MaterialPageRoute<int>(
+        builder: (BuildContext context) {
+          return Scaffold(
+            body: PopScope<Object?>(
+              canPop: false,
+              onPopInvoked: (bool didPop, Object? result) {
+                receivedResult = result;
+              },
+              child: const Center(
+                child: Text('new page'),
+              ),
+            ),
+          );
+        },
+      ),
+    );
+    await tester.pumpAndSettle();
+    expect(find.text('new page'), findsOneWidget);
+
+    nav.currentState!.maybePop(poppedResult);
+    await tester.pumpAndSettle();
+    expect(receivedResult, poppedResult);
+  },
+    variant: TargetPlatformVariant.all(),
+  );
+
   testWidgets('toggling canPop on secondary route allows/prevents backs', (WidgetTester tester) async {
     final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
     bool canPop = true;
@@ -115,9 +203,9 @@
               builder: (BuildContext context, StateSetter stateSetter) {
                 oneContext = context;
                 setState = stateSetter;
-                return PopScope(
+                return PopScope<Object?>(
                   canPop: canPop,
-                  onPopInvoked: (bool didPop) {
+                  onPopInvoked: (bool didPop, Object? result) {
                     lastPopSuccess = didPop;
                   },
                   child: const Center(
@@ -271,7 +359,7 @@
                 if (!usePopScope) {
                   return child;
                 }
-                return const PopScope(
+                return const PopScope<Object?>(
                   canPop: false,
                   child: child,
                 );
@@ -314,12 +402,12 @@
               return Column(
                 children: <Widget>[
                   if (usePopScope1)
-                    const PopScope(
+                    const PopScope<Object?>(
                       canPop: false,
                       child: Text('hello'),
                     ),
                   if (usePopScope2)
-                    const PopScope(
+                    const PopScope<Object?>(
                       canPop: false,
                       child: Text('hello'),
                     ),