Close the search by pressing escape (#167648)

<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->

Close
[showSearch()](https://api.flutter.dev/flutter/material/showSearch.html)
by pressing escape button.

feat #148146 


## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [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.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
This commit is contained in:
Arthur Monteiro Alves Melo 2025-04-25 15:03:12 -03:00 committed by GitHub
parent c0c12a759e
commit 293c2c2f10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 1 deletions

View File

@ -344,6 +344,15 @@ abstract class SearchDelegate<T> {
..pop(result);
}
/// Closes the search page and returns to the underlying route whitout result.
void _pop(BuildContext context) {
_currentBody = null;
_focusNode?.unfocus();
Navigator.of(context)
..popUntil((Route<dynamic> route) => route == _route)
..pop();
}
/// The hint text that is shown in the search field when it is empty.
///
/// If this value is set to null, the value of
@ -506,7 +515,16 @@ class _SearchPage<T> extends StatefulWidget {
class _SearchPageState<T> extends State<_SearchPage<T>> {
// This node is owned, but not hosted by, the search page. Hosting is done by
// the text field.
FocusNode focusNode = FocusNode();
late final FocusNode focusNode = FocusNode(
onKeyEvent: (FocusNode node, KeyEvent event) {
// When the user presses the escape key, close the search page.
if (event is KeyDownEvent && event.logicalKey == LogicalKeyboardKey.escape) {
widget.delegate._pop(context);
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
);
@override
void initState() {

View File

@ -137,6 +137,47 @@ void main() {
expect(find.text('Suggestions'), findsOneWidget);
});
testWidgets('Can close search with escape button and return null', (WidgetTester tester) async {
final _TestSearchDelegate delegate = _TestSearchDelegate();
addTearDown(() => delegate.dispose());
final List<String?> selectedResults = <String?>[];
await tester.pumpWidget(TestHomePage(delegate: delegate, results: selectedResults));
// We are on the homepage.
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
// Open search.
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
expect(find.text('Bottom'), findsOneWidget);
// Simulate escape button.
await simulateKeyDownEvent(LogicalKeyboardKey.escape, platform: 'windows');
await tester.pumpAndSettle();
expect(selectedResults, <String?>[null]);
// We are on the homepage again.
expect(find.text('HomeBody'), findsOneWidget);
expect(find.text('HomeTitle'), findsOneWidget);
expect(find.text('Suggestions'), findsNothing);
// Open search again.
await tester.tap(find.byTooltip('Search'));
await tester.pumpAndSettle();
expect(find.text('HomeBody'), findsNothing);
expect(find.text('HomeTitle'), findsNothing);
expect(find.text('Suggestions'), findsOneWidget);
});
testWidgets('Hint text color overridden', (WidgetTester tester) async {
const String searchHintText = 'Enter search terms';
final _TestSearchDelegate delegate = _TestSearchDelegate(searchHint: searchHintText);