Repository navigation
fix(naked_ui): keep focus on the caller's control in a popover opened without tap - #338
Open
leoafarias wants to merge 3 commits into
Open
leoafarias wants to merge 3 commits into
leoafarias wants to merge 3 commits into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
This was referenced Oct 27, 2026
leoafarias
force-pushed
the
fix/naked-ui-popover-trigger-focus
branch
from
October 29, 2026 20:12
23376b9 to
45f6619
Compare
… without tap NakedPopover(openOnTap: false) wrapped its child in an internal Focus bound to the return node. A button passed as the child therefore had an extra tab stop ahead of it, focus returned to the wrapper instead of the button when the popover closed, and passing one node as both triggerFocusNode and the button's focusNode threw "Cannot get renderObject of inactive element". With openOnTap: false the child is now the caller's own control: the popover adds no Focus, and triggerFocusNode (or a Focus child's node) goes only to RawMenuAnchor.childFocusNode, as NakedMenu does. Without a node, closing returns focus to whatever had it before opening. The default openOnTap: true is unchanged. RemixPopover(openOnTap: false) inherits the fix.
…control Scope the triggerFocusNode doc to openOnTap: false and note that a nested popover only gets its node focused by the outermost RawMenuAnchor. Name the behavior changes in the CHANGELOG, hold the example's FocusNode in a State and dispose it, drop a test the other shared-node tests already cover, and note that sharing one node needs naked_ui 1.3.0 or later.
leoafarias
changed the base branch from
main
to
fix/slider-lifecycle-geometry
October 29, 2026 21:40
leoafarias
force-pushed
the
fix/naked-ui-popover-trigger-focus
branch
from
October 29, 2026 21:40
45f6619 to
c976353
Compare
This was referenced Oct 29, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
NakedPopover(openOnTap: false)wrapped its child in an internalFocus(focusNode: returnNode). With a caller's button as the child, that wrapper added a tab stop ahead of the button, took focus back when the popover closed instead of the button, and madetriggerFocusNodeand the button's ownfocusNodecollide (passing one node as both threw "Cannot get renderObject of inactive element").RemixPopover(openOnTap: false, child: RemixButton(...))inherited all of it, and a screen reader landed on an unlabelled focusable wrapper after the popover closed.With
openOnTap: falsethe child is now the caller's own control, mirroringNakedMenu.openOnTap: false(#335): the popover adds noFocus, andtriggerFocusNode(or aFocuschild's node) goes only toRawMenuAnchor.childFocusNode. AttachtriggerFocusNodeto the control, using the same node as itsfocusNode. Without a node, closing returns focus to whatever had it before the popover opened; inside another popover or menu overlay,RawMenuAnchorfocuses the node only for the outermost anchor. The defaultopenOnTap: trueis unchanged.Documents the pattern in
NakedPopoveranddocs/components/popover.mdx, and adds a naked_ui CHANGELOG fix entry. Caller-owned mode explicitly does not promise automatic expanded/collapsed semantics. The Remix adapter documentation and its three button regressions are preserved in draft #352 for Agent B’s #339 batch.Merge note: sharing one node between
triggerFocusNodeand the control'sfocusNodethrows on published naked_ui 1.2.3, and the docs now recommend it (with a "needs naked_ui 1.3.0 or later" note). Merge this right before the naked_ui 1.3.0 release, and raise remix'snaked_uifloor with that release.Validation
RemixPopovertests with aRemixButtonchild (all fail before the fix).ci:checkspassed. Base: naked-only fix(slider): reconcile interactions and share track geometry #334; followed by release chore(release): naked_ui 1.3.0 #353.Pinned Flutter 3.44.0 validation on the rebuilt release stack: full naked_ui suite 1,157 passed / 3 skipped, all
melos run ci:checkspassed, andgit diff --checkpassed. The isolated Remix draft applied above this stack passes 204 adapter tests.Mutation evidence (all runtime failures, no compile failures): ignoring the marked rail fails all 9 geometry tests; retaining the pointer transaction through configuration changes fails 11 lifecycle tests; removing excluded-semantics focus fails its keyboard test (expected
[51], got no change); restoring the old focus-list comparison fails 5 lifecycle tests; restoring the old popover docs promise fails the docs/runtime probe (documented collapsed state versus runtimeTristate.none). Restoring the fixes passes all 48 focused tests. Mutation probes and logs remain uncommitted under.context/.dart analyze(no new issues), rootdart format,melos run ci:checks,melos run docs:check.Related Issues
Found while building the Remix menu control trigger (#337).
Checklist
///).Breaking Change
No API change. With
openOnTap: falsethe popover no longer attachestriggerFocusNodeitself, so a child that cannot take focus (such asText, or a disabled button the wrapper used to make focusable) is no longer a tab stop, a node not attached to the control gets no focus, and with aFocuschild and a differenttriggerFocusNodethe child's own node is the tab stop. These match the existing meaning ofopenOnTap: false(the caller owns activation and focus).