TabIndicatorScope


Scope for the composable used to render a Tab indicator, this can be used for more complex indicators requiring layout information about the tabs like TabRowDefaults.PrimaryIndicator and TabRowDefaults.SecondaryIndicator

Summary

Public functions

Modifier

A layout modifier that provides tab positions, this can be used to animate and layout a TabIndicator depending on size, position, and content size of each Tab.

Cmn
Modifier
Modifier.tabIndicatorOffset(
    selectedTabIndex: Int,
    matchContentSize: Boolean
)

A Modifier that follows the default offset and animation

Cmn

Public functions

tabIndicatorLayout

fun Modifier.tabIndicatorLayout(
    measure: MeasureScope.(Measurable, Constraints, List<TabPosition>) -> MeasureResult
): Modifier

A layout modifier that provides tab positions, this can be used to animate and layout a TabIndicator depending on size, position, and content size of each Tab.

import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.VectorConverter import androidx.compose.animation.core.spring import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Tab import androidx.compose.material3.TabPosition import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.layout.Measurable import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp val colors =  listOf(  MaterialTheme.colorScheme.primary,  MaterialTheme.colorScheme.secondary,  MaterialTheme.colorScheme.tertiary,  ) var startAnimatable by remember { mutableStateOf<Animatable<Dp, AnimationVector1D>?>(null) } var endAnimatable by remember { mutableStateOf<Animatable<Dp, AnimationVector1D>?>(null) } val coroutineScope = rememberCoroutineScope() val indicatorColor: Color by animateColorAsState(colors[index % colors.size], label = "") Box(  Modifier.tabIndicatorLayout {  measurable: Measurable,  constraints: Constraints,  tabPositions: List<TabPosition> ->  val newStart = tabPositions[index].left  val newEnd = tabPositions[index].right  val startAnim =  startAnimatable  ?: Animatable(newStart, Dp.VectorConverter).also { startAnimatable = it }  val endAnim =  endAnimatable  ?: Animatable(newEnd, Dp.VectorConverter).also { endAnimatable = it }  if (endAnim.targetValue != newEnd) {  coroutineScope.launch {  endAnim.animateTo(  newEnd,  animationSpec =  if (endAnim.targetValue < newEnd) {  spring(dampingRatio = 1f, stiffness = 1000f)  } else {  spring(dampingRatio = 1f, stiffness = 50f)  },  )  }  }  if (startAnim.targetValue != newStart) {  coroutineScope.launch {  startAnim.animateTo(  newStart,  animationSpec =  // Handle directionality here, if we are moving to the right, we  // want the right side of the indicator to move faster, if we are  // moving to the left, we want the left side to move faster.  if (startAnim.targetValue < newStart) {  spring(dampingRatio = 1f, stiffness = 50f)  } else {  spring(dampingRatio = 1f, stiffness = 1000f)  },  )  }  }  val indicatorEnd = endAnim.value.roundToPx()  val indicatorStart = startAnim.value.roundToPx()  // Apply an offset from the start to correctly position the indicator around the tab  val placeable =  measurable.measure(  constraints.copy(  maxWidth = indicatorEnd - indicatorStart,  minWidth = indicatorEnd - indicatorStart,  )  )  layout(constraints.maxWidth, constraints.maxHeight) {  placeable.place(indicatorStart, 0)  }  }  .padding(5.dp)  .fillMaxSize()  .drawWithContent {  drawRoundRect(  color = indicatorColor,  cornerRadius = CornerRadius(5.dp.toPx()),  style = Stroke(width = 2.dp.toPx()),  )  } )

tabIndicatorOffset

fun Modifier.tabIndicatorOffset(
    selectedTabIndex: Int,
    matchContentSize: Boolean = false
): Modifier

A Modifier that follows the default offset and animation

Parameters
selectedTabIndex: Int

the index of the current selected tab

matchContentSize: Boolean = false

this modifier can also animate the width of the indicator \ to match the content size of the tab.