`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',