After many hours of debugging and code-simplification, my app looks like the following:
just a bunch of unpopulated cards that you can navigate around using direction arrows, the problem is that if you move fast enough (some strokes per second), the app crashes with the following exception:
java.lang.IllegalStateException: LayoutCoordinate operations are only valid when isAttached is true
at androidx.compose.ui.node.NodeCoordinator.getParentLayoutCoordinates(NodeCoordinator.kt:255)
at androidx.compose.ui.layout.LayoutCoordinatesKt.findRootCoordinates(LayoutCoordinates.kt:180)
at androidx.compose.ui.focus.FocusTraversalKt.focusRect(FocusTraversal.kt:126)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.findBestCandidate-4WY_MpI(TwoDimensionalFocusSearch.kt:223)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.searchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:166)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.generateAndSearchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:141)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.searchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:173)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.access$searchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:1)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt$generateAndSearchChildren$1.invoke(TwoDimensionalFocusSearch.kt:148)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt$generateAndSearchChildren$1.invoke(TwoDimensionalFocusSearch.kt:146)
at androidx.tv.foundation.lazy.list.LazyLayoutBeyondBoundsModifierLocal.layout-o7g1Pn8(LazyBeyondBoundsModifier.kt:185)
at androidx.compose.ui.focus.BeyondBoundsLayoutKt.searchBeyondBounds--OM-vw8(BeyondBoundsLayout.kt:45)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.generateAndSearchChildren-4C6V_qg(TwoDimensionalFocusSearch.kt:146)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.twoDimensionalFocusSearch--OM-vw8(TwoDimensionalFocusSearch.kt:74)
at androidx.compose.ui.focus.TwoDimensionalFocusSearchKt.twoDimensionalFocusSearch--OM-vw8(TwoDimensionalFocusSearch.kt:70)
at androidx.compose.ui.focus.FocusTraversalKt.focusSearch-sMXa3k8(FocusTraversal.kt:106)
at androidx.compose.ui.focus.FocusOwnerImpl.moveFocus-3ESFkO8(FocusOwnerImpl.kt:180)
at androidx.compose.ui.platform.AndroidComposeView$keyInputModifier$1.invoke-ZmokQxo(AndroidComposeView.android.kt:242)
at androidx.compose.ui.platform.AndroidComposeView$keyInputModifier$1.invoke(AndroidComposeView.android.kt:237)
at androidx.compose.ui.input.key.KeyInputNode.onKeyEvent-ZmokQxo(KeyInputModifier.kt:80)
at androidx.compose.ui.focus.FocusOwnerImpl.dispatchKeyEvent-ZmokQxo(FocusOwnerImpl.kt:215)
at androidx.compose.ui.platform.AndroidComposeView.dispatchKeyEvent(AndroidComposeView.android.kt:696)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1959)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1959)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1959)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1959)
at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:476)
at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1861)
at android.app.Activity.dispatchKeyEvent(Activity.java:4085)
at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.kt:103)
at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:85)
at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.kt:117)
at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:390)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5960)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5828)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5310)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5367)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5333)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5498)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5341)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5555)
2024-07-21 18:04:54.371 10613-10613 MessageQueue-JNI com.android.exttv E at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5314)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5367)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5333)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5341)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5314)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5367)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5333)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5531)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5689)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3287)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2829)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2820)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3264)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:335)
at android.os.Looper.loop(Looper.java:183)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
This is what the code look at this point:
import android.app.Activity
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Card
@Composable
fun CatalogBrowser(
context: Activity,
) {
val rows = List(4) { rowIndex ->
List(20) { columnIndex -> "Item $rowIndex-$columnIndex" }
}
TvLazyColumn(modifier = Modifier.fillMaxSize()) {
items(rows) { rowItems ->
Column(modifier = Modifier.padding(16.dp)) {
TvLazyRow {
items(rowItems) { item ->
Card(
modifier = Modifier
.padding(40.dp) // Space between cards
.width(150.dp).height(200.dp), // Size of each card
onClick ={}
) {
}}}}}}
}
It looks like that having
Column(modifier = Modifier.padding(16.dp))
is the ultimate reason of the crash. Now, because i don’t like to waste hours in debug without learning anything, would anyone be kind enough to explain me what is wrong with Column
placed in that position? Is it a bug in the libraries? Should I raise this issue with Google?
P.S. I’m not sure about the reason why I had a Column
inside a TvLazyColumn
, I’m still learning Composable and Kotlin and it may have been a copy paste error, but nevertheless, this bug was really hard to catch