Skip to content

Implement keep-focus-on-submit on Android and report focus state for press dispatch - #115

Merged
simonhamp merged 7 commits into
mainfrom
fix/335-keep-focus-on-submit
Oct 2, 2026
Merged

simonhamp merged 7 commits into
mainfrom
fix/335-keep-focus-on-submit

Conversation

@simonhamp

Copy link
Copy Markdown
Member

Companion to NativePHP/mobile-air#521. Supersedes #53 by @gwleuverink. The two need to land together.

Why a new PR. #53 was opened from a fork owned by an organisation. GitHub only lets maintainers push to forks owned by a user, so the fixes from @shanerbaner82's review couldn't be added to it. This branch is Willem's two commits from #53, a merge of current main, and the fixes on top. His description in #53 covers the original change.

What it does

This is the input-renderer half of the keyboard dismissal fix for NativePHP/mobile-air#335.

On Android the text inputs never parsed keep_focus_on_submit, and supplying KeyboardActions had replaced Compose's default hide-on-Done. The prop is now parsed, and submit clears focus by default and keeps it with the attribute, in all three input variants. On both platforms the inputs report their focus state to core's KeyboardFocusPolicy. The iOS input also registers a flush hook, so a press can push the field's latest text to PHP first.

Changes since #53

Focus token and teardown. Inputs register with fieldFocused / fieldBlurred and a per-field token instead of writing the policy's flags. That fixes the stale flag when focus moved from a Filled field to a Bare one with the attribute, where Bare wrote synchronously and Filled wrote later from a collector. It also stops a Bare field clearing another field's state on first composition. Inputs release the policy when they leave the tree while focused.

Controls go through the policy. On Android a control consumes the tap, so nothing dropped the keyboard for it. These now call dismissForInteractiveTap(): button, list item (row, long press, trailing press, and the leading and trailing checkbox, radio and switch), icon, pressable text, chip, checkbox, radio, toggle, accordion, button group, tab row, and the select and date picker triggers.

On iOS core's root gesture handles dismissal. Button, the list item's trailing press, chip press and pressable text now send their press through dispatchPress, so a pending text change reaches PHP before the press.

Core constraint. nativephp/mobile is bumped to ^4.6, the release that ships KeyboardFocusPolicy. CI here stays red until 4.6 is tagged.

Tested

Built and run against the core branch on an iPhone 17 simulator and an Android emulator. The table of results is in the core PR. Pest passes (267 tests).

Button group and tab row are compiled but were not on the test screen.

For the reviewer

  • Left alone on purpose: menu items and menu triggers, nav and tab bar chrome, modals, drawers, sheets, swipe actions, refresh, sliders, and anything inside a text input.
  • On Android, opening a select or a date picker drops the keyboard even when the focused field has keep-focus-on-submit. Both open their own window, which takes the keyboard.
  • The inline date picker (picker-style inline or wheel) handles its own taps, so tapping a day cell leaves the keyboard up on Android.
  • ListRenderer hides the keyboard on vertical drag without asking the policy. That predates this PR.

🤖 Generated with Claude Code

gwleuverink and others added 7 commits August 17, 2026 19:30
Follows the KeyboardFocusPolicy change in mobile-air #353. The inputs
now call fieldFocused / fieldBlurred with a per-field token instead of
writing the policy's flags.

On Android, Bare set the flag synchronously while Filled and Outlined
set it later from an interaction collector, so moving focus from a
Filled field to a Bare one with keep-focus-on-submit left the flag
false. A Bare field's first composition also wrote false over whatever
another field had set. With the token a blur only counts when it comes
from the field that owns the policy. Filled and Outlined read the
keep-focus prop through rememberUpdatedState, since their effect keeps
the props of the first composition.

All three Android inputs and the iOS input also release the policy when
they leave the tree, so a field popped while focused doesn't leave a
flush closure behind.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Buttons, list items, icons, chips with on_press and pressable text runs
sent their press straight to the bridge, so they skipped the policy
that @press on a plain container goes through.

On Android the control consumes the tap, so nothing dropped the
keyboard. These handlers now call dismissForInteractiveTap() first,
which clears focus unless the focused field keeps it.

On iOS the root gesture in core handles dismissal, but the press still
needs dispatchPress so a pending text change, such as an autocorrection
the tap just committed, reaches PHP before the press does.

Menu items, nav chrome, refresh, modal dismiss, swipe actions and the
selection controls are left as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The renderers now reference KeyboardFocusPolicy, which first ships in
the core release that includes mobile-air #353. Without the bump an app
that updates this plugin on an older core fails to compile on both
platforms.

4.6 is the assumed version for that release. Adjust if it ships under a
different number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On iOS the root gesture in core drops the keyboard for any tap a
control claims. On Android the control consumes the tap, so each one
has to ask the policy itself. Buttons, list items, icons and pressable
text already did. This adds the same call to checkbox, radio, toggle,
chip, accordion, button group, tab row, the list item's leading and
trailing selection controls, and the select and date picker triggers.

As before, a focused field with keep-focus-on-submit keeps the
keyboard. The select and date picker are the exception: they open
their own window, which takes the keyboard whatever the field asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants