TabRow

Functions summary

Unit
@Composable
TabRow(
    selectedTabIndex: Int,
    modifier: Modifier,
    containerColor: Color,
    contentColor: Color,
    separator: @Composable () -> Unit,
    indicator: @Composable (tabPositions: List<DpRect>, doesTabRowHaveFocus: Boolean) -> Unit,
    tabs: @Composable TabRowScope.() -> Unit
)

TV-Material Design Horizontal TabRow

Functions

@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
): Unit

TV-Material Design Horizontal TabRow

Display all tabs in a set simultaneously and if the tabs exceed the container size, it has scrolling to navigate to next tab. They are best for switching between related content quickly, such as between transportation methods in a map. To navigate between tabs, use d-pad left or d-pad right when focused.

A TvTabRow contains a row of []s, and displays an indicator underneath the currently selected tab. A TvTabRow places its tabs offset from the starting edge, and allows scrolling to tabs that are placed off screen.

Examples:

import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Tab
import androidx.tv.material3.TabRow
import androidx.tv.material3.Text

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),
                )
            }
        }
    }
}
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Tab
import androidx.tv.material3.TabDefaults
import androidx.tv.material3.TabRow
import androidx.tv.material3.TabRowDefaults
import androidx.tv.material3.Text

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),
                )
            }
        }
    }
}
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Tab
import androidx.tv.material3.TabRow
import androidx.tv.material3.Text

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),
                )
            }
        }
    }
}
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.Tab
import androidx.tv.material3.TabRow
import androidx.tv.material3.TabRowDefaults
import androidx.tv.material3.Text

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),
                    )
                }
            }
        }
    }
}
Parameters
selectedTabIndex: Int

the index of the currently selected tab

modifier: Modifier = Modifier

the Modifier to be applied to this tab row

containerColor: Color = TabRowDefaults.ContainerColor

the color used for the background of this tab row

contentColor: Color = TabRowDefaults.contentColor()

the primary color used in the tabs

separator: @Composable () -> Unit = { TabRowDefaults.TabSeparator() }

use this composable to add a separator between the tabs

indicator: @Composable (tabPositions: List<DpRect>, doesTabRowHaveFocus: Boolean) -> Unit = @Composable { tabPositions, doesTabRowHaveFocus -> tabPositions.getOrNull(selectedTabIndex)?.let { currentTabPosition -> TabRowDefaults.PillIndicator( currentTabPosition = currentTabPosition, doesTabRowHaveFocus = doesTabRowHaveFocus, ) } }

used to indicate which tab is currently selected and/or focused. This lambda provides 2 values:

  • tabPositions: list of DpRect which provides the position of each tab

  • doesTabRowHaveFocus: whether any Tab within TabRow is focused

tabs: @Composable TabRowScope.() -> Unit

a composable which will render all the tabs