How can I create a dots indicator like this for a HorizontalPager in Jetpack Compose?
I found a few libs and examples, but non of them were animated like this.
A copy-paste solution with as many customizations as I can think of.
Result
Usage
@Composable
fun PageIndicatorSample() {
val numberOfPages = 3
val (selectedPage, setSelectedPage) = remember {
mutableStateOf(0)
}
// NEVER use this, this is just for example
LaunchedEffect(
key1 = selectedPage,
) {
delay(3000)
setSelectedPage((selectedPage + 1) % numberOfPages)
}
PageIndicator(
numberOfPages = numberOfPages,
selectedPage = selectedPage,
defaultRadius = 60.dp,
selectedLength = 120.dp,
space = 30.dp,
animationDurationInMillis = 1000,
)
}
PageIndicator
@Composable
fun PageIndicator(
numberOfPages: Int,
modifier: Modifier = Modifier,
selectedPage: Int = 0,
selectedColor: Color = Color.Blue,
defaultColor: Color = Color.LightGray,
defaultRadius: Dp = 20.dp,
selectedLength: Dp = 60.dp,
space: Dp = 30.dp,
animationDurationInMillis: Int = 300,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(space),
modifier = modifier,
) {
for (i in 0 until numberOfPages) {
val isSelected = i == selectedPage
PageIndicatorView(
isSelected = isSelected,
selectedColor = selectedColor,
defaultColor = defaultColor,
defaultRadius = defaultRadius,
selectedLength = selectedLength,
animationDurationInMillis = animationDurationInMillis,
)
}
}
}
PageIndicatorView
@Composable
fun PageIndicatorView(
isSelected: Boolean,
selectedColor: Color,
defaultColor: Color,
defaultRadius: Dp,
selectedLength: Dp,
animationDurationInMillis: Int,
modifier: Modifier = Modifier,
) {
val color: Color by animateColorAsState(
targetValue = if (isSelected) {
selectedColor
} else {
defaultColor
},
animationSpec = tween(
durationMillis = animationDurationInMillis,
)
)
val width: Dp by animateDpAsState(
targetValue = if (isSelected) {
selectedLength
} else {
defaultRadius
},
animationSpec = tween(
durationMillis = animationDurationInMillis,
)
)
Canvas(
modifier = modifier
.size(
width = width,
height = defaultRadius,
),
) {
drawRoundRect(
color = color,
topLeft = Offset.Zero,
size = Size(
width = width.toPx(),
height = defaultRadius.toPx(),
),
cornerRadius = CornerRadius(
x = defaultRadius.toPx(),
y = defaultRadius.toPx(),
),
)
}
}
Also shared the same in this blog - Page Indicator with Jetpack Compose using Canvas and animations