TabRow

Composable Component

TV-Material Design Horizontal TabRow

Android
@Composable
fun TabRow(
    selectedTabIndex: Int,
    modifier: Modifier = Modifier,
    containerColor: Color = TabRowDefaults.ContainerColor,
    contentColor: Color = TabRowDefaults.contentColor(),
    separator: @Composable () -> Unit = { TabRowDefaults.TabSeparator() },
    indicator: @Composable (tabPositions: List<DpRect>, doesTabRowHaveFocus: Boolean) -> Unit =
        @Composable { tabPositions, doesTabRowHaveFocus ->
            tabPositions.getOrNull(selectedTabIndex)?.let { currentTabPosition ->
                TabRowDefaults.PillIndicator(
                    currentTabPosition = currentTabPosition,
                    doesTabRowHaveFocus = doesTabRowHaveFocus
                )
            }
        },
    tabs: @Composable TabRowScope.() -> Unit
)

Parameters

selectedTabIndexthe index of the currently selected tab
modifierthe Modifier to be applied to this tab row
containerColorthe color used for the background of this tab row
contentColorthe primary color used in the tabs
separatoruse this composable to add a separator between the tabs
indicatorused to indicate which tab is currently selected and/or focused. This lambda provides 2 values:
tabsa composable which will render all the tabs

Code Examples

OnClickNavigation

/** Tab changes onClick instead of onFocus */
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun OnClickNavigation() {
    val bgColors =
        listOf(
            Color(0x6a, 0x16, 0x16),
            Color(0x6a, 0x40, 0x16),
            Color(0x6a, 0x6a, 0x16),
            Color(0x40, 0x6a, 0x16),
        )
    var focusedTabIndex by remember { mutableStateOf(0) }
    var activeTabIndex by remember { mutableStateOf(focusedTabIndex) }
    Box(modifier = Modifier.fillMaxSize().background(bgColors[activeTabIndex])) {
        TabRow(
            selectedTabIndex = focusedTabIndex,
            indicator = { tabPositions, doesTabRowHaveFocus ->
                // FocusedTab's indicator
                TabRowDefaults.PillIndicator(
                    currentTabPosition = tabPositions[focusedTabIndex],
                    activeColor = Color.Blue.copy(alpha = 0.4f),
                    inactiveColor = Color.Transparent,
                    doesTabRowHaveFocus = doesTabRowHaveFocus,
                )
                // SelectedTab's indicator
                TabRowDefaults.PillIndicator(
                    currentTabPosition = tabPositions[activeTabIndex],
                    doesTabRowHaveFocus = doesTabRowHaveFocus,
                )
            },
            modifier = Modifier.focusRestorer()
        ) {
            repeat(bgColors.size) {
                key(it) {
                    Tab(
                        selected = activeTabIndex == it,
                        onFocus = { focusedTabIndex = it },
                        onClick = {
                            focusedTabIndex = it
                            activeTabIndex = it
                        }
                    ) {
                        Text(
                            text = "Tab ${it + 1}",
                            fontSize = 12.sp,
                            modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
                        )
                    }
                }
            }
        }
    }
}

PillIndicatorTabRow

/** Tab row with a Pill indicator */
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun PillIndicatorTabRow() {
    val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
    var selectedTabIndex by remember { mutableStateOf(0) }
    TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
        tabs.forEachIndexed { index, tab ->
            key(index) {
                Tab(
                    selected = index == selectedTabIndex,
                    onFocus = { selectedTabIndex = index },
                ) {
                    Text(
                        text = tab,
                        fontSize = 12.sp,
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
                    )
                }
            }
        }
    }
}

TabRowWithDebounce

/** Tab row with delay between tab changes */
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun TabRowWithDebounce() {
    val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
    var selectedTabIndex by remember { mutableStateOf(0) }
    // This index will be used to show a panel
    var tabPanelIndex by remember { mutableStateOf(selectedTabIndex) }
    // Change the tab-panel only after some delay
    LaunchedEffect(selectedTabIndex) {
        delay(250.microseconds)
        tabPanelIndex = selectedTabIndex
    }
    TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
        tabs.forEachIndexed { index, tab ->
            key(index) {
                Tab(
                    selected = index == selectedTabIndex,
                    onFocus = { selectedTabIndex = index },
                ) {
                    Text(
                        text = tab,
                        fontSize = 12.sp,
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
                    )
                }
            }
        }
    }
}

UnderlinedIndicatorTabRow

/** Tab row with an Underlined indicator */
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
fun UnderlinedIndicatorTabRow() {
    val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
    var selectedTabIndex by remember { mutableStateOf(0) }
    TabRow(
        selectedTabIndex = selectedTabIndex,
        indicator = { tabPositions, doesTabRowHaveFocus ->
            TabRowDefaults.UnderlinedIndicator(
                currentTabPosition = tabPositions[selectedTabIndex],
                doesTabRowHaveFocus = doesTabRowHaveFocus,
            )
        },
        modifier = Modifier.focusRestorer()
    ) {
        tabs.forEachIndexed { index, tab ->
            key(index) {
                Tab(
                    selected = index == selectedTabIndex,
                    onFocus = { selectedTabIndex = index },
                    colors = TabDefaults.underlinedIndicatorTabColors(),
                ) {
                    Text(
                        text = tab,
                        fontSize = 12.sp,
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
                    )
                }
            }
        }
    }
}

Create your own Component Library

Material Components are meant to be used as is and they do not allow customizations. To build your own Jetpack Compose component library use Compose Unstyled