`SelectableRegion` can dismiss context menu with keyboard shortcuts (#184788)

Part of #182628

Adds support for `DismissIntent` in `SelectableRegion`, so it may
dismiss the context menu when it is present and the "esc" key is
pressed.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart
index 59de8ba..0bb751f 100644
--- a/packages/flutter/lib/src/widgets/selectable_region.dart
+++ b/packages/flutter/lib/src/widgets/selectable_region.dart
@@ -393,6 +393,7 @@
         granularity: TextGranularity.document,
       ),
     ),
+    DismissIntent: CallbackAction<DismissIntent>(onInvoke: _hideToolbarIfVisible),
   };
 
   final Map<Type, GestureRecognizerFactory> _gestureRecognizers =
@@ -1831,6 +1832,14 @@
     }
   }
 
+  Object? _hideToolbarIfVisible(DismissIntent intent) {
+    if (_selectionOverlay?.toolbarIsVisible ?? false) {
+      hideToolbar(false);
+      return null;
+    }
+    return Actions.invoke(context, intent);
+  }
+
   @override
   void selectAll([SelectionChangedCause? cause]) {
     clearSelection();
diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart
index f7bd407..e4e8c9b 100644
--- a/packages/flutter/test/widgets/selectable_region_test.dart
+++ b/packages/flutter/test/widgets/selectable_region_test.dart
@@ -5428,6 +5428,49 @@
     skip: kIsWeb, // [intended] Web uses its native context menu.
   );
 
+  testWidgets(
+    'can hide context menu with DismissIntent',
+    (WidgetTester tester) async {
+      final toolbarKey = UniqueKey();
+      await tester.pumpWidget(
+        TestWidgetsApp(
+          home: SelectableRegion(
+            selectionControls: testTextSelectionHandleControls,
+            contextMenuBuilder:
+                (BuildContext context, SelectableRegionState selectableRegionState) {
+                  return SizedBox.shrink(key: toolbarKey);
+                },
+            child: const Text('How are you?'),
+          ),
+        ),
+      );
+      await tester.pumpAndSettle();
+
+      final RenderParagraph paragraph = tester.renderObject<RenderParagraph>(
+        find.descendant(of: find.text('How are you?'), matching: find.byType(RichText)),
+      );
+      final TestGesture gesture = await tester.startGesture(
+        textOffsetToPosition(paragraph, 6),
+      ); // at the 'r'
+      addTearDown(gesture.removePointer);
+      await tester.pump(const Duration(milliseconds: 500));
+      // `are` is selected.
+      expect(paragraph.selections[0], const TextSelection(baseOffset: 4, extentOffset: 7));
+      await tester.pumpAndSettle();
+
+      await gesture.up();
+      await tester.pumpAndSettle();
+      // Context menu has appeared.
+      expect(find.byKey(toolbarKey), findsOneWidget);
+
+      // Hide the context menu using the DismissIntent.
+      await tester.sendKeyEvent(LogicalKeyboardKey.escape);
+      await tester.pump();
+      expect(find.byKey(toolbarKey), findsNothing);
+    },
+    skip: kIsWeb, // [intended] Web uses its native context menu.
+  );
+
   // Regression test for https://github.com/flutter/flutter/issues/121053.
   testWidgets(
     'Ensure SelectableRegion does not affect the layout of its children',