Repository navigation
[flutter] when primary mouse pointers don't contact a focused node, reset the focus - #82575
Conversation
|
This currently only applies for keyboard events, I'd need to do more research into how touch events should behave |
|
Gold has detected about 2 untriaged digest(s) on patchset 2. |
|
adding another widget here seems to be changing the layout slightly, which is unfortunate... |
|
This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again. For more guidance, visit Writing a golden file test for Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. |
| child: Builder( | ||
| builder: (BuildContext context) { | ||
| return widget.route.buildPage( | ||
| return _FocusTrap(child: widget.route.buildPage( |
There was a problem hiding this comment.
any reason we scope at route level?
There was a problem hiding this comment.
i scoped it at the route level so that I can have this and the shortcuts widget used by the SelectionArea widget use the same focus node.
| FocusNode get focusNode => widget.focusNode ?? _focusNode; | ||
|
|
||
| void _stealFocus(TapDownDetails details) { | ||
| if (details.kind != PointerDeviceKind.mouse) |
There was a problem hiding this comment.
do you know what is the native behavior for mobile if you have an external mouse?
There was a problem hiding this comment.
i don't have access to the hardware to test this. we could definitely just disable the behavior on iOS/android until we could test it
There was a problem hiding this comment.
(disabled for non-desktop platforms for now)
|
Just an outsiders thought: This looks great and all, but should it really be necessary to add a |
|
I agree, I'm not sure that this is the right solution exactly but i'm not sure how else you would do it. Widgets can't unfocus themselves if they aren't hit 🤔 |
|
In HTML Web every element can be focused. Even if it is not editable, correct? I am not a flutter developer. So this is only an idea from outside but I wanted to post it. For me it is rather important to get some working solution in the next two weeks than to get a really good solution. |
|
An html element isn't equivalent to a flutter widget. A widget not have any UI, only configuring other widgets themselves. Even making every widget with uI focusable would make pure focus traversal painful. |
|
Based on some discussion with @gspencergoog , I've updated this to use the unfocus functionality of the modal route's FocusScopeNode. Of course, using the gesture recognizer the way that I am is breaking the world, so I'll need to find a way to make this change not compete with any other recognizers somehow... |
|
Gold has detected about 3 untriaged digest(s) on patchset 4. |
| /// Creates a tap gesture recognizer. | ||
| BaseTapGestureRecognizer({ Object? debugOwner }) | ||
| : super(deadline: kPressTimeout , debugOwner: debugOwner); | ||
| BaseTapGestureRecognizer({ Object? debugOwner, Set<PointerDeviceKind>? supportedDevices }) |
There was a problem hiding this comment.
Will revert, unrelated
| _requestKeyboard(); | ||
| }, | ||
| onDidGainAccessibilityFocus: handleDidGainAccessibilityFocus, | ||
| return FocusArea( |
There was a problem hiding this comment.
Because we want clicks on the decorations of at text field to retain focus
|
PTAL at the current approach |
chunhtai
left a comment
There was a problem hiding this comment.
LGTM except for the share focusScopeNode in two widgets.
| ignoring: ignoreEvents, | ||
| child: child, | ||
| child: _FocusTrap( | ||
| focusScopeNode: focusScopeNode, |
There was a problem hiding this comment.
The same focusnode get passed into two widgets. not sure if this is a concern cc @gspencergoog
If it is, we can avoid this by merging the FocusScope and _focusTrap into one widget.
There was a problem hiding this comment.
Usually sharing a FocusNode (or FocusScopeNode) between two widgets is a concern because they'll fight over the attachment. If the focus node is only used to call requestFocus(), or something else that doesn't require attachment, in one of the widgets, then it's technically OK.
Basically, the rule is: Don't pass the same focus node to two widgets that will attach it to the focus tree. Only one widget can "own" it.
You might also have lifetime issues when it comes to disposing them. Again, only one may dispose of the focus node, and so the widget that it is passed to that doesn't own it needs to have a shorter lifetime than the one that does own it.
There was a problem hiding this comment.
I think what I'm doing here is safe, no stack traces or bugs so far. What would you like me to change about the current structure?
There was a problem hiding this comment.
I don't have a good suggestion neither, if @gspencergoog is ok with the current structure, I am fine with it as well
There was a problem hiding this comment.
I think I'm OK with the current structure: the widget using the node is a private class, so we have control over its lifetime, and it doesn't appear to be doing anything that would attach the node to the focus tree.
| || event.buttons != kPrimaryButton | ||
| || event.kind != PointerDeviceKind.mouse | ||
| || _shouldIgnoreEvents | ||
| || currentFocus == null) |
There was a problem hiding this comment.
nit: Can you put braces on this conditional block? Having a really long multi-line conditional just reads weird without the braces.
| ignoring: ignoreEvents, | ||
| child: child, | ||
| child: _FocusTrap( | ||
| focusScopeNode: focusScopeNode, |
There was a problem hiding this comment.
I think I'm OK with the current structure: the widget using the node is a private class, so we have control over its lifetime, and it doesn't appear to be doing anything that would attach the node to the focus tree.
|
FYI @renyou , not sure what is up with the google testing on this PR |
|
It seems some google internal tests may be very slow and I am going to increase the timeout threshold. Hopefully it can resolve this. |
|
@jonahwilliams Did you trigger a full test on this PR? |
|
yes, since it is a potentially a substantial change I wanted to be sure we ran as many tests as possible... |
|
Yes! The tests are actually all done, just took longer to analyze all the test results. I manually set the results. Everything looks good! |
|
🎊 |
|
@jonahwilliams - does this PR address #74354? |
|
This only handles clicks via mouse on desktop and mobile web, not taps - though the PR also mentions clicks. I'm not sure if that is trying to fix the same issue as this or a different input connection based focus issue |
|
|
If we want this behaviour, why not just put a Focus widget around the route? ...but I'm not convinced we want this behaviour. It's not what happens on Windows and macOS as far as I can tell. |
|
I think this is just something we want on Web, and on Web the behaviour is just that the root of the tree is itself focusable, no need to check whether a tap would have focused a node and all that. |

Fixes #82515
Fixes #32433
Introduces a widget that checks if a pointer down event has hit the currently focused nodes' render object. This leads to odd behavior with text field decorations due to the focus being only owned by the inner editable text widget.
The modal route's focus scoped node is used to "reset" the focus when pointer did not contact either.
This is limited to mouse pointers on desktop and mobile web.