Search code examples
androidtabsandroid-jetpack-compose

How to properly update list of tabs compose


I get a list of folders from the network and display it using tabs. How can I correctly redraw the tabs when I receive a new list? It may be a different size and with different folder names.

val selectedTabIndex = remember { mutableIntStateOf(0) }
    
        val tabs = 
            listOf("Folder 1", "Folder 2", "Folder 3", "Folder 4", "Folder 5", "Folder 6", "Folder 7")
                .toMutableStateList()
    
        Row {
            ScrollableTabRow(
                selectedTabIndex = selectedTabIndex.intValue,
                contentColor = Color.LightGray,
                edgePadding = 0.dp
            ) {
                tabs.forEachIndexed { tabIndex, tab ->
                    Tab(
                        selected = selectedTabIndex.intValue == tabIndex,
                        onClick = { selectedTabIndex.intValue = tabIndex },
                        text = { Text(text = tab) }
                    )
                }
            }
        }

Solution

  • You declared tabs as follows:

    val tabs = listOf("Folder 1", "Folder 2", "Folder 3").toMutableStateList()
    

    This is equivalent to the more common syntax as follows:

    val tabs = remember { mutableStateListOf("Folder 1", "Folder 2", "Folder 3") }
    

    As a result, Jetpack Compose will properly observe the changes that happen to the tabs variable. Whenever you change the list by adding or removing an item, this will trigger a recomposition, and all Composables that depend on the tabs variable will be redrawn.

    tabs.add("Folder 8")
    

    As a result,

    • the forEachIndexed function will be re-executed and
    • thus the Tab Composables will be updated.

    However, you might want to make sure manually that selectedTabIndex does not point to an invalid index. As of now, if you remove items from the tabs list, it could happen that no Tab is selected anymore. You can try it as follows:

    val tabs = ...
    
    // use "by" keyword to directly access the value property
    var selectedTabIndex by remember { mutableStateOf(0) }
    // at every Recomposition, we make sure that the selectedTabIndex is in range
    selectedTabIndex = selectedTabIndex.coerceIn(0, tabs.size - 1)
    

    A small side note, once you add some content to the tabs, you might will need to use the key function to properly preserve the state when adding or removing tabs.