Reputation: 119
So I'm trying to improve the keyboard focus indication for Jetpack Compose components.
I have created a custom class to draw border:
private class MyHighlightIndicationNode(private val interactionSource: InteractionSource) :
Modifier.Node(), DrawModifierNode {
private var isFocused = false
override fun onAttach() {
coroutineScope.launch {
var focusCount = 0
interactionSource.interactions.collect { interaction ->
when (interaction) {
is FocusInteraction.Focus -> focusCount++
is FocusInteraction.Unfocus -> focusCount--
}
val focused = focusCount > 0
if (isFocused != focused) {
isFocused = focused
invalidateDraw()
}
}
}
}
override fun ContentDrawScope.draw() {
drawContent()
if (isFocused) {
drawRoundRect(
color = themedIndicationColor,
size = size,
cornerRadius = CornerRadius(12.dp.toPx(), 12.dp.toPx()),
style = Stroke(width = 2.dp.toPx()),
alpha = 1.0f
)
}
}
}
object MyHighlightIndication : IndicationNodeFactory {
override fun create(interactionSource: InteractionSource): DelegatableNode {
return MyHighlightIndicationNode(interactionSource)
}
override fun hashCode(): Int = -1
override fun equals(other: Any?) = other === this
}
It's from this link
Now, if a Modifier has a toggleable/clickable/selectable interaction, I saw that the border gets removed after the action.
Here is client code:
@Composable
private fun GoodExample7(
snackbarLauncher: SnackbarLauncher?
) {
val cardClickMessage = stringResource(id = R.string.custom_focus_indicators_example_7_message)
val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
var isCheck by remember { mutableStateOf(false) }
OutlinedCard(
onClick = {
snackbarLauncher?.show(cardClickMessage)
},
modifier = Modifier
.padding(top = 8.dp)
.indication(
interactionSource = interactionSource,
indication = VisibleFocusIndication(
themedIndicationColor = MaterialTheme.colorScheme.primary
)
).toggleable(
value = isCheck,
onValueChange = { isCheck = it },
role = Role.Checkbox,
interactionSource = interactionSource,
indication = ripple()
)
,
// Key technique: Provide a common interactionSource to both clickable Card and indication.
interactionSource = interactionSource
) {
Row (
horizontalArrangement = Arrangement.Center,
modifier = Modifier.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
BodyText(
textId = R.string.custom_focus_indicators_example_7_card_description,
modifier = Modifier
)
Checkbox(checked = isCheck, onCheckedChange = null)
}
}
}
Expected: After the click event, it should keep showing the Border indication. An additional issue is if there is some other update, say, a progress bar update, in those cases, it also loses the focus from the element.
I've used some boilerplate code. Any idea how this can be solved?
Upvotes: 0
Views: 26
Reputation: 119
I have found the issue. The issue was I was creating an instance of the Focusable class. Without remember
scope. So the class was rerendering resulting is loosing focus.
Upvotes: 0