fix(mobile): focus the search field when search opens (#9120)

Opening search from Home now focuses the search field, so the keyboard
comes up right away and you can start typing without tapping the field
first. This applies when `SearchPage` opens as its own route; the
embedded variant is unchanged. Closing with X and reopening works as
before (the previous query is kept and the field is focused again).

### Validation

- Extended the existing open-and-close journey in
`search_page_test.dart`: it now opens search without tapping the field
and asserts the field has focus and the keyboard is shown. The step
fails with the production change reverted.
- Full mobile test suite and checks passed in the pre-push hook.
- Automated run on an iOS 27 simulator (iPhone 17 Pro) signed in to a
real community: opened search from Home (keyboard up, caret in the
field), typed a query and got channel and message results, closed with
X, reopened (keyboard up again). The recording contains internal
community content, so it was shared internally rather than here.
- Human testing waived under the author's standing exception; the
automated simulator run above covers it.

buzz-review-completed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Tom Brow <tomb@squareup.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Tom Brow
2026-11-05 11:49:48 -08:00
committed by GitHub
parent 9aa70124ba
commit 6793cbd25b
2 changed files with 14 additions and 3 deletions

View File

@@ -101,6 +101,16 @@ class SearchPage extends HookConsumerWidget {
void activateSearch() => focusNode.requestFocus();
// Opening search as its own route means the user wants to type, so raise
// the keyboard right away instead of waiting for a tap on the field.
useEffect(() {
if (embedded) return null;
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context.mounted) focusNode.requestFocus();
});
return null;
}, const []);
useEffect(() {
final reselection = tabReselection;
if (reselection == null) return null;

View File

@@ -252,7 +252,7 @@ void main() {
debugDefaultTargetPlatformOverride = null;
});
testWidgets('X dismisses search while the keyboard is focused', (
testWidgets('opening search focuses the field; X dismisses it', (
tester,
) async {
await tester.pumpWidget(
@@ -278,8 +278,9 @@ void main() {
);
await tester.tap(find.text('Open search'));
await tester.pumpAndSettle();
await tester.tap(find.byType(TextField));
await tester.pumpAndSettle();
final field = tester.widget<TextField>(find.byType(TextField));
expect(field.focusNode!.hasFocus, isTrue);
expect(tester.testTextInput.isVisible, isTrue);
await tester.enterText(find.byType(TextField), 'draft');
await tester.tap(find.byTooltip('Close search'));
await tester.pumpAndSettle();