diff --git a/packages/flutter/lib/src/material/search_anchor.dart b/packages/flutter/lib/src/material/search_anchor.dart index 5ba923a2830..0964bd27c05 100644 --- a/packages/flutter/lib/src/material/search_anchor.dart +++ b/packages/flutter/lib/src/material/search_anchor.dart @@ -695,6 +695,14 @@ class _SearchViewRoute extends PopupRoute<_SearchViewRoute> { updateTweens(anchorKey.currentContext!); toggleVisibility?.call(); viewOnClose?.call(); + // Unfocus the anchor to prevent the Enter key from triggering unwanted + // actions (like route pops) when the view closes and focus returns to + // the anchor's search bar. + WidgetsBinding.instance.addPostFrameCallback((_) { + if (anchorKey.currentContext != null) { + FocusScope.of(anchorKey.currentContext!).unfocus(); + } + }); return super.didPop(result); } diff --git a/packages/flutter/test/material/search_anchor_test.dart b/packages/flutter/test/material/search_anchor_test.dart index c8212fd8334..1aaa30c8c6d 100644 --- a/packages/flutter/test/material/search_anchor_test.dart +++ b/packages/flutter/test/material/search_anchor_test.dart @@ -995,9 +995,75 @@ void main() { await tester.testTextInput.receiveAction(TextInputAction.done); expect(onSubmittedCalled, 1); expect(controller.isOpen, false); + }); + // Regression test for https://github.com/flutter/flutter/issues/178719. + testWidgets('SearchAnchor.bar anchor loses focus after view closes', (WidgetTester tester) async { + final controller = SearchController(); + addTearDown(controller.dispose); + var onSubmittedCalled = 0; + await tester.pumpWidget( + MaterialApp( + home: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Center( + child: Material( + child: SearchAnchor.bar( + searchController: controller, + onSubmitted: (String value) { + setState(() { + onSubmittedCalled++; + }); + controller.closeView(value); + }, + suggestionsBuilder: (BuildContext context, SearchController controller) { + return []; + }, + ), + ), + ); + }, + ), + ), + ); + + // Tap to open the search view. + await tester.tap(find.byType(SearchBar)); + await tester.pumpAndSettle(); + expect(controller.isOpen, true); + + // Find the anchor SearchBar's TextField. + final Finder anchorTextField = find.descendant( + of: find.byType(SearchBar).first, + matching: find.byType(TextField), + ); + + // Find the view SearchBar's TextField. + final Finder viewTextField = find.descendant( + of: findViewContent(), + matching: find.byType(TextField), + ); + + // View SearchBar should have focus. + expect(tester.widget(viewTextField).focusNode?.hasFocus, isTrue); + + // Close the view (use receiveAction because sendKeyEvent + // doesn't trigger onSubmitted in tests - the text input action needs to + // be sent directly to properly simulate the IME behavior). await tester.testTextInput.receiveAction(TextInputAction.done); - expect(onSubmittedCalled, 2); + await tester.pumpAndSettle(); + expect(onSubmittedCalled, 1); + expect(controller.isOpen, false); + + // After search view is closed, anchor SearchBar should NOT have focus. + expect(tester.widget(anchorTextField).focusNode?.hasFocus, isFalse); + + // Simulate the IME behavior via input action again . + // It should not trigger onSubmitted because field is unfocused + // and the text input connection is closed. + await tester.testTextInput.receiveAction(TextInputAction.done); + await tester.pump(); + expect(onSubmittedCalled, 1); // Still 1, not incremented. }); testWidgets('hintStyle can override textStyle for hintText', (WidgetTester tester) async {