Alex Styl
02/11/2025, 7:30 AMModifier.focusable()
exactly?
I was under the impression that I was telling compose that a composable can be focused, but it seems like using it multiple times on the same composable breaks focus requesters.
Code in thread.
My use case is:
I am building a drag and drop modifier. I am moving focus to the item that has been picked. I was also adding focusable()
on it but it seems like it is breaking the focus requesting for anyone else.Alex Styl
02/11/2025, 7:31 AMval requester = remember { FocusRequester() }
Box(
Modifier
.size(90.dp)
.background(Color.Red)
.focusRequester(requester)
.focusable() // <-----
.onFocusChanged { state -> println("Has focused = ${state}") }
.clickable {
requester.requestFocus()
}
)
When i run the above code w/o the focusable, then clicking will grab focus. It prints:
Has focused = Inactive //<- after launch
Has focused = Active //<- after click
But, when i add the focusable() modifier, then clicking will grab focus and then mark is as inactive:
Has focused = Inactive // <- after launch
Has focused = Active // <- after click
Has focused = Inactive //<- immediately shown after the above
Alex Styl
02/11/2025, 9:47 AMText(
text = "Here is some text",
modifier = Modifier
.clickable {
fs.requestFocus() //<- requesting focus, focuses the clickable because it's the first 'focusable'
}
.focusRequester(fs)
.onFocusChanged { focused ->
if (focused.isFocused) {
error("IS FOCUSED")
}
}
)
On the other hand if the focus clickable is after the on Focus changed this crashes:
Text(
text = "Here is some text",
modifier = Modifier
.onFocusChanged { focused ->
if (focused.isFocused) {
error("IS FOCUSED")
}
}
.clickable {
fs.requestFocus() // <- requesting focus focuses the Text() because it's the first focusable()
}
.focusRequester(fs)
)
Alex Styl
02/11/2025, 9:47 AMLouis Pullen-Freilich [G]
02/11/2025, 3:32 PMfocusable
to be a focusTarget()
- focusable adds lots of different logic on top, but purely for interacting with the focus system in this way it’s just a focusTarget
under the hood.
I was under the impression that I was telling compose that a composable can be focused, but it seems like using it multiple times on the same composable breaks focus requesters.When you add a
focusTarget()
modifier, you are adding a focus node in the tree. This is a node that can be focused, and receive information about focus events. So if you use focusable
/ focusTarget
multiple times, you are just adding multiple distinct focusable nodes in the tree.
This is because modifiers don’t ‘set’ properties on a layout or something, for example why if you do Modifier.padding(10.dp).padding(10.dp) this is actually 20.dp of padding, not 10.Louis Pullen-Freilich [G]
02/11/2025, 3:36 PMfocusRequester
and onFocusEvent
, they basically apply to the nearest child focus target in the hierarchy. So in your original example, the problem as you noticed is that when you add the new focusable modifier, you now basically have two independently focusable nodes on this box, and the focus requester only applies to one of them (the focusable), not the clickableLouis Pullen-Freilich [G]
02/11/2025, 3:38 PMOk i think i figured it out.
It’s about the order of the Modifiers.
This will cause the ‘clickable()’ to grab the focus. so this won’t crash:
On the other hand if the focus clickable is after the on Focus changed this crashes:But I’m not sure I understand this part, and what the clickable ‘crashing’ signifies. It should be the other way around, the first one won’t request focus to the clickable, because the clickable is above the requester
Louis Pullen-Freilich [G]
02/11/2025, 3:39 PMAlex Styl
02/12/2025, 3:21 AMAlex Styl
02/12/2025, 3:21 AMLouis Pullen-Freilich [G]
02/12/2025, 4:16 AMAlex Styl
02/12/2025, 4:23 AMAlex Styl
02/12/2025, 4:43 AMyschimke
02/12/2025, 9:07 AMyschimke
02/12/2025, 9:08 AM{ requester.requestFocus() }
is a bug. And it's a pretty common sample. Your control via clicking is fine.yschimke
02/12/2025, 9:09 AMHierarchicalFocusCoordinator()
and calling requestFocus on your behalf, tied into lifecycle. So off screen composition doesn't steel it