Carousel
Android
Component in Tv Material Compose
Composes a hero card rotator to highlight a piece of content.
Note: The animations and focus management features have been dropped temporarily due to some technical challenges. If you need them, consider using the previous version of the library (1.0.0-alpha10) or kindly wait until the next alpha version (1.1.0-alpha01).
Last updated:
Installation
dependencies {
implementation("androidx.tv:tv-material:1.0.0")
}
Overloads
@ExperimentalTvMaterial3Api
@Composable
fun Carousel(
itemCount: Int,
modifier: Modifier = Modifier,
carouselState: CarouselState = rememberCarouselState(),
autoScrollDurationMillis: Long = CarouselDefaults.TimeToDisplayItemMillis,
contentTransformStartToEnd: ContentTransform = CarouselDefaults.contentTransform,
contentTransformEndToStart: ContentTransform = CarouselDefaults.contentTransform,
carouselIndicator: @Composable BoxScope.() -> Unit = {
CarouselDefaults.IndicatorRow(
itemCount = itemCount,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
)
},
content: @Composable AnimatedContentScope.(index: Int) -> Unit
)
Parameters
name | description |
---|---|
modifier | Modifier applied to the Carousel. |
itemCount | total number of items present in the carousel. |
carouselState | state associated with this carousel. |
autoScrollDurationMillis | duration for which item should be visible before moving to the next item. |
contentTransformStartToEnd | animation transform applied when we are moving from start to end in the carousel while scrolling to the next item |
contentTransformEndToStart | animation transform applied when we are moving from end to start in the carousel while scrolling to the next item |
carouselIndicator | indicator showing the position of the current item among all items. |
content | defines the items for a given index. |
Code Examples
SimpleCarousel
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
fun SimpleCarousel() {
@Composable
fun Modifier.onFirstGainingVisibility(onGainingVisibility: () -> Unit): Modifier {
var isVisible by remember { mutableStateOf(false) }
LaunchedEffect(isVisible) { if (isVisible) onGainingVisibility() }
return onPlaced { isVisible = true }
}
@Composable
fun Modifier.requestFocusOnFirstGainingVisibility(): Modifier {
val focusRequester = remember { FocusRequester() }
return focusRequester(focusRequester).onFirstGainingVisibility {
focusRequester.requestFocus()
}
}
val backgrounds =
listOf(
Color.Red.copy(alpha = 0.3f),
Color.Yellow.copy(alpha = 0.3f),
Color.Green.copy(alpha = 0.3f)
)
var carouselFocused by remember { mutableStateOf(false) }
Carousel(
itemCount = backgrounds.size,
modifier =
Modifier.height(300.dp).fillMaxWidth().onFocusChanged {
carouselFocused = it.isFocused
},
contentTransformEndToStart = fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
contentTransformStartToEnd = fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
Box(
modifier =
Modifier.background(backgrounds[itemIndex])
.border(2.dp, Color.White.copy(alpha = 0.5f))
.fillMaxSize()
) {
var buttonFocused by remember { mutableStateOf(false) }
val buttonModifier =
if (carouselFocused) {
Modifier.requestFocusOnFirstGainingVisibility()
} else {
Modifier
}
Button(
onClick = {},
modifier =
buttonModifier
.onFocusChanged { buttonFocused = it.isFocused }
.padding(40.dp)
.border(
width = 2.dp,
color = if (buttonFocused) Color.Red else Color.Transparent,
shape = RoundedCornerShape(50)
)
// Duration of animation here should be less than or equal to carousel's
// contentTransform duration to ensure the item below does not disappear
// abruptly.
.animateEnterExit(
enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
exit = slideOutHorizontally(animationSpec = tween(1000))
)
.padding(vertical = 2.dp, horizontal = 5.dp)
) {
Text(text = "Play")
}
}
}
}
CarouselIndicatorWithRectangleShape
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalAnimationApi::class)
@Composable
fun CarouselIndicatorWithRectangleShape() {
val backgrounds =
listOf(
Color.Red.copy(alpha = 0.3f),
Color.Yellow.copy(alpha = 0.3f),
Color.Green.copy(alpha = 0.3f)
)
val carouselState = rememberCarouselState()
Carousel(
itemCount = backgrounds.size,
modifier = Modifier.height(300.dp).fillMaxWidth(),
carouselState = carouselState,
carouselIndicator = {
CarouselDefaults.IndicatorRow(
itemCount = backgrounds.size,
activeItemIndex = carouselState.activeItemIndex,
modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
indicator = { isActive ->
val activeColor = Color.Red
val inactiveColor = activeColor.copy(alpha = 0.5f)
Box(
modifier =
Modifier.size(8.dp)
.background(
color = if (isActive) activeColor else inactiveColor,
shape = RectangleShape,
),
)
}
)
},
contentTransformEndToStart = fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000))),
contentTransformStartToEnd = fadeIn(tween(1000)).togetherWith(fadeOut(tween(1000)))
) { itemIndex ->
Box(
modifier =
Modifier.background(backgrounds[itemIndex])
.border(2.dp, Color.White.copy(alpha = 0.5f))
.fillMaxSize()
) {
var isFocused by remember { mutableStateOf(false) }
Button(
onClick = {},
modifier =
Modifier.onFocusChanged { isFocused = it.isFocused }
// Duration of animation here should be less than or equal to carousel's
// contentTransform duration to ensure the item below does not disappear
// abruptly.
.animateEnterExit(
enter = slideInHorizontally(animationSpec = tween(1000)) { it / 2 },
exit = slideOutHorizontally(animationSpec = tween(1000))
)
.padding(40.dp)
.border(
width = 2.dp,
color = if (isFocused) Color.Red else Color.Transparent,
shape = RoundedCornerShape(50)
)
.padding(vertical = 2.dp, horizontal = 5.dp)
) {
Text(text = "Play")
}
}
}
}