I am new with android Compose Can Anyone told me if there any way to implement Page indcater in compose without third library ? I am using material design 3 I have tried many solutions But its Dublicated
I try a way with state pager but now its Dublicated 🙂
After many attempts to find an effective solution I found two ways
The first: Jetpack Compose animated pager dots indicator?
The Second : Pager indicator for compose
but you will face some issue about PagerState Class which now is duplicated so you Can use rememberPagerState
fun PageIndicatorSample() {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(40.dp))
val pagerState1 = rememberPagerState(initialPage = 0)
val coroutineScope = rememberCoroutineScope()
PagerIndicatornew(pagerState = pagerState1, indicatorCount = getAllData.size) {
coroutineScope.launch {
pagerState1.scrollToPage(it)
}
}
HorizontalPager(
pageCount = getAllData.size,
state = pagerState1,
) {
// Here You Add what compose You Want... this is just example
Card(getAllData[it])
}
}
}
For Pager indicator
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun PagerIndicatornew(
modifier: Modifier = Modifier,
pagerState: PagerState,
indicatorCount: Int = 5,
indicatorSize: Dp = 16.dp,
indicatorShape: Shape = CircleShape,
space: Dp = 8.dp,
activeColor: Color = Color(0xffEC407A),
inActiveColor: Color = Color.LightGray,
onClick: ((Int) -> Unit)? = null) {
val listState = rememberLazyListState()
val totalWidth: Dp = indicatorSize * indicatorCount + space * (indicatorCount - 1)
val widthInPx = LocalDensity.current.run { indicatorSize.toPx() }
val currentItem by remember {
derivedStateOf {
pagerState.currentPage
}
}
val itemCount = indicatorCount
LaunchedEffect(key1 = currentItem) {
val viewportSize = listState.layoutInfo.viewportSize
listState.animateScrollToItem(
currentItem,
(widthInPx / 2 - viewportSize.width / 2).toInt()
)
}
LazyRow(
modifier = modifier.width(totalWidth),
state = listState,
contentPadding = PaddingValues(vertical = space),
horizontalArrangement = Arrangement.spacedBy(space),
userScrollEnabled = false
) {
items(itemCount) { index ->
val isSelected = (index == currentItem)
// Index of item in center when odd number of indicators are set
// for 5 indicators this is 2nd indicator place
val centerItemIndex = indicatorCount / 2
val right1 =
(currentItem < centerItemIndex &&
index >= indicatorCount - 1)
val right2 =
(currentItem >= centerItemIndex &&
index >= currentItem + centerItemIndex &&
index <= itemCount - centerItemIndex + 1)
val isRightEdgeItem = right1 || right2
// Check if this item's distance to center item is smaller than half size of
// the indicator count when current indicator at the center or
// when we reach the end of list. End of the list only one item is on edge
// with 10 items and 7 indicators
// 7-3= 4th item can be the first valid left edge item and
val isLeftEdgeItem =
index <= currentItem - centerItemIndex &&
currentItem > centerItemIndex &&
index < itemCount - indicatorCount + 1
Box(
modifier = Modifier
.graphicsLayer {
val scale = if (isSelected) {
1f
} else if (isLeftEdgeItem || isRightEdgeItem) {
.5f
} else {
.8f
}
scaleX = scale
scaleY = scale
}
.clip(indicatorShape)
.size(indicatorSize)
.background(
if (isSelected) activeColor else inActiveColor,
indicatorShape
)
.then(
if (onClick != null) {
Modifier
.clickable {
onClick.invoke(index)
}
} else Modifier
)
)
}
}
}
NOTE: the first solution has some issues with indexing so I prefer the Second one