I have been experiencing very erratic jumping when using Jetpack Compose’s LazyColumns on Android TV. D-Pad navigation is supposed to be supported in Compose for a while now, but it seems simple cases are not supported—or I am doing something terribly wrong when setting a custom focus overlay.
The follow code results in what is shown on this video. As you can see, I am simply navigating step by step from top to bottom but the focused item jumps very randomly in between. It feels like the number are reproducible, but I have not stopped to write them down to verify.
@Composable
fun Greeting(listItems: List<Int>) {
var currentItem by remember { mutableStateOf("None") }
val scrollState = rememberLazyListState()
val scope = rememberCoroutineScope()
Row {
Text(
text = "Current Focus = $currentItem",
modifier = Modifier.weight(1f)
)
Column(Modifier.weight(1f)) {
Text(text = "With focus changed")
LazyColumn(state = scrollState) {
itemsIndexed(listItems) { index, item ->
Item(
item,
{ currentItem = "Left $item" },
Modifier.onFocusChanged { focusState ->
scope.launch {
if (focusState.isFocused) {
val visibleItemsInfo = scrollState.layoutInfo.visibleItemsInfo
val visibleSet = visibleItemsInfo.map { it.index }.toSet()
if (index == visibleItemsInfo.last().index) {
scrollState.scrollToItem(index)
} else if (visibleSet.contains(index) && index != 0) {
scrollState.scrollToItem(index - 1)
}
}
}
}
)
}
}
}
Column(Modifier.weight(1f)) {
Text(text = "Without focus changed")
LazyColumn {
items(listItems) { item ->
Item(
item,
{ currentItem = "Right $item" }
)
}
}
}
}
}
@Composable
fun Item(
item: Int,
onFocused: () -> Unit,
modifier: Modifier = Modifier,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
val focused by interactionSource.collectIsFocusedAsState()
Text("$item", modifier = modifier
.onFocusChanged { state ->
if (state.isFocused) {
onFocused()
}
}
.focusable(true, interactionSource)
.padding(8.dp)
.border(if (focused) 4.dp else 0.dp, MaterialTheme.colors.primary)
.padding(8.dp)
)
}
At first I thought I was doing something incorrectly and it is recomposing but different ways of checking the focus as well as just using plain buttons which already have a focus state (a very bad one for TV tbf) results in the exact same issue.
After reporting this to Google, it turns out that it actually was a bug in Jetpack Compose, which was fixed in the latest version 1.3.0-rc01.