I am working on accessibility implementation (blind disabilities) and once a user swipes left or right in order to move a focus on the item within the LazyRow, I need to know the item index in the list which currently in the accessibility focus.
I have such an impl:
LazyRow(
state = listState,
modifier = modifier,
flingBehavior = rememberSnapFlingBehavior(lazyListState = listState)
) {
itemsIndexed(items = episodes, key = { index, _ ->
"$CARD_KEY$index"
}) { index, item ->
item.apply {
CustomCard(
episode = item.episode,
modifier = Modifier
.wrapContentWidth()
.background(
BrandedTheme._ui_core__bottomsheet__backgroundColor
)
.onFocusChanged {
if (it.isFocused) {
if (listState.isScrollInProgress.not() && selectedEpisodeIndex != -1) {
GlobalScope.launch {
listState.scrollToItem(index = index)
}
}
}
}
.wrapContentHeight()
.semantics(mergeDescendants = true) {
isTraversalGroup = true
traversalIndex = -1f
},
index = index
)
}
}
}
So, each item in the list has a modifier with the property - onFocusChanged, the problem is that this event triggers only when the list is initializing, but then when a user moves an accessibility focus (by swiping left/right) I see (on the screen) that the next item gets a focus, but in the code onFocusChanged is not triggered.
What am I missing here?
UPD
Added onFocusEvent() and focusTarget(), now it seems like I'm receiving the event of changing elements, but there are 2 problems:
- All elements have the state
Inactive(none active). - The index does not display the index of the element currently on the screen.
LazyRow(
state = listState,
modifier = modifier,
flingBehavior = rememberSnapFlingBehavior(lazyListState = listState)
) {
itemsIndexed(items = episodes, key = { index, _ ->
"$CARD_KEY$index"
}) { index, item ->
item.apply {
CustomCard(
episode = item.episode,
modifier = Modifier
.wrapContentWidth()
.onFocusEvent {
Log.e("HERE", "111 onFocusEvent: $index :: $it")
}
.background(
BrandedTheme._ui_core__bottomsheet__backgroundColor
)
.onFocusChanged {
if (it.isFocused) {
if (listState.isScrollInProgress.not() && selectedEpisodeIndex != -1) {
GlobalScope.launch {
listState.scrollToItem(index = index)
}
}
}
}
.focusTarget()
.wrapContentHeight()
.semantics(mergeDescendants = true) {
isTraversalGroup = true
traversalIndex = -1f
},
index = index
)
}
}
}
Another idea I had was to create a custom rememberSnapFlingBehavior because I noticed that in accessibility mode, the focus on the screen switches and it pulls the next element, but it doesn't center it on the screen (although this works without accessibility mode). But in the end, this idea didn't work either.
If I'm not mistaken, Compose doesn't propagate focus changes to the parent composable by default (in order to improve performance). In your case, you might need to manually propagate the focus changes by passing a callback function from the parent composable.
Also, when traversalIndex is set to -1f, I don't believe the item is included in the accessibility focus traversal, which might also be part of the problem. Have you tried setting
traversalIndex = index.toFloat()?