'Dynamic Tabs creation using jetpack compose

How to create dynamic tabs inside the jetpack compose, in my application the number of tabs i need to show depends upon API response. But when i am setting the TabsRow, we need to set the selectedTabIndex value, and this is causing the issue. Because before the tabs data get loaded, it's trying to build the ui without any tab inside.

Please help me to fix this or please guide me to do it in right ways.

@Composable
fun CustomTab(viewModel: BeatsViewModel) {
    var tabIndex by remember { mutableStateOf(0) }
    val beats = viewModel.beatsData.observeAsState().value
    ScrollableTabRow(selectedTabIndex = tabIndex) {
        beats?.forEachIndexed { index, beatData ->
            val selected = tabIndex == index
            Tab(selected = selected, onClick = {
                tabIndex = index
            }) {
                Column(
                    modifier = Modifier
                        .padding(10.dp)
                        .height(50.dp)
                        .fillMaxWidth(),
                    verticalArrangement = Arrangement.SpaceBetween
                ) {
                    Box(
                        Modifier
                            .size(24.dp)
                            .align(Alignment.CenterHorizontally)
                            .background(color = if (selected) Color.Red else Color.White)
                    )
                    Text(
                        text = beatData.mBeatName,
                        style = MaterialTheme.typography.body1,
                        modifier = Modifier.align(Alignment.CenterHorizontally)
                    )
                }
            }
        }
    }
}

This is throwing the error as below (even before my viewmodel post any value to beatsData) -

Process: in.bizom.android, PID: 23133
    java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.get(ArrayList.java:437)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$1.invoke(TabRow.kt:230)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$1.invoke(TabRow.kt:228)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$2$1$2$3.invoke(TabRow.kt:299)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$2$1$2$3.invoke(TabRow.kt:298)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:251)
        at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2$1$1.invoke(SubcomposeLayout.kt:251)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.ComposerKt.invokeComposable(Composer.kt:3337)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2582)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2571)
        at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:247)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2571)
        at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:2522)
        at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:478)
        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:748)
        at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:2987)
        at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:2987)
        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:433)
        at androidx.compose.ui.layout.SubcomposeLayoutState.subcomposeInto(SubcomposeLayout.kt:269)
        at androidx.compose.ui.layout.SubcomposeLayoutState.access$subcomposeInto(SubcomposeLayout.kt:154)
        at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:244)
        at androidx.compose.ui.layout.SubcomposeLayoutState$subcompose$2.invoke(SubcomposeLayout.kt:241)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.withNoObservations(SnapshotStateObserver.kt:142)
        at androidx.compose.ui.node.OwnerSnapshotObserver.withNoSnapshotReadObservation$ui_release(OwnerSnapshotObserver.kt:55)
        at androidx.compose.ui.node.LayoutNode.withNoSnapshotReadObservation$ui_release(LayoutNode.kt:1175)
        at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:241)
        at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose(SubcomposeLayout.kt:235)
        at androidx.compose.ui.layout.SubcomposeLayoutState.subcompose$ui_release(SubcomposeLayout.kt:224)
        at androidx.compose.ui.layout.SubcomposeLayoutState$Scope.subcompose(SubcomposeLayout.kt:490)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$2$1$2.invoke(TabRow.kt:298)
        at androidx.compose.material.TabRowKt$ScrollableTabRow$2$1$2.invoke(TabRow.kt:273)
        at androidx.compose.ui.layout.MeasureScope$layout$1.placeChildren(MeasureScope.kt:68)
        at androidx.compose.ui.layout.SubcomposeLayoutState$createMeasurePolicy$1$measure$1.placeChildren(SubcomposeLayout.kt:367)
        at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:993)
        at androidx.compose.ui.node.LayoutNode$layoutChildren$1.invoke(LayoutNode.kt:978)


Solution 1:[1]

You need to define an empty state view for that screen. How would it look like before the data is loaded from the API/ database? You can use a loading indicator, a message, etc. But you need to account for that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 cd1