'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 |
