Hey, I am trying to prevent the compose material s...
# compose
v
Hey, I am trying to prevent the compose material slider from being focusable like this:
Slider(modifier = Modifier.focusProperties { canFocus = false }, ...)
but it doesn't seem to have any effect in some of my layouts, while it works in others. What could explain the difference and is this even the proper way to do it? EDIT: I just realized what causes this if someone else encounters a similar problem. After dumping the focus modifier tree from the debugger I found out something is overriding the provided focusProperties, because canFocus was true in a certain parent layout. Turns out there was a parent layout element which had a disabled clickable modifier:
.clickable { enabled = false }
. A relevant internal implementation detail here is that the clickable modifier also applies a
focusableInNonTouchMode
modifier. More implementation details in 🧵 because this is getting quite long...
🔥 2
🫡 2
This is the source:
Copy code
internal fun Modifier.focusableInNonTouchMode(
    enabled: Boolean,
    interactionSource: MutableInteractionSource?
) = composed(
...
) {
    val inputModeManager = LocalInputModeManager.current
    Modifier
        .focusProperties { canFocus = inputModeManager.inputMode != InputMode.Touch }
        .focusable(enabled, interactionSource)
}
And
Modifier.focusable
in turn only applies a
.focusTarget()
if it is enabled
A
.focusTarget()
in turn applies a
ResetFocusModifierLocals
after itself to stop the propagation of focusProperties down the node tree.
With all this information, it's now easy to see what happened. Focus properties are always applied in the order of child -> parent, so the
.focusProperties
modifier that is applied in
.focusableInNonTouchMode
regardless of whether it is enabled or not leaks down the tree and overrided the focusProperties I was providing to the Slider composable.
d
sorry for the noob question, but how did you dump the focus tree?
v
Not a noob question at all, it's not trivial
I set a breakpoint in the debugger and used the debugger to evaluate something like this:
Copy code
data class Dump(val rect: Rect, val state: FocusState, val properties: FocusProperties, val nodeItself: FocusModifier); fun traverse(node: FocusModifier): List<Any> { return listOf(Dump(node.focusRect(), node.focusState, node.focusProperties, node), node.children.map { traverse(it) }) }; traverse(focusModifier)