Carousel
Composable Component
Composes a hero card rotator to highlight a piece of content.
Android
@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
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
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")
}
}
}
}
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")
}
}
}
}
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