I am using Accompanist's paging indicator. It is working fine but it is not customizable. I want to set exact amount of dots should be visible in screen and those dots should be size configurable on scrolls. There is another question similar to this thread but owner already accepted AndroidView approach which I want it Composable way.
I made a sample looks similar, logic for scaling is raw but it looks similar. Need to convert from
@OptIn(ExperimentalPagerApi::class)
@Composable
fun PagerIndicator(
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 = pagerState.pageCount
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
)
)
}
}
}
Usage
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(40.dp))
val pagerState1 = rememberPagerState(initialPage = 0)
val coroutineScope = rememberCoroutineScope()
PagerIndicator(pagerState = pagerState1) {
coroutineScope.launch {
pagerState1.scrollToPage(it)
}
}
HorizontalPager(
count = 10,
state = pagerState1,
) {
Box(
modifier = Modifier
.padding(10.dp)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center
) {
Text(
"Text $it",
fontSize = 40.sp,
color = Color.Gray
)
}
}
val pagerState2 = rememberPagerState(initialPage = 0)
PagerIndicator(
pagerState = pagerState2,
indicatorSize = 20.dp,
indicatorCount = 7,
activeColor = Color(0xff2196F3),
inActiveColor = Color(0xffBBDEFB),
indicatorShape = CutCornerShape(10.dp)
)
HorizontalPager(
count = 10,
state = pagerState2,
) {
Box(
modifier = Modifier
.padding(10.dp)
.shadow(1.dp, RoundedCornerShape(8.dp))
.background(Color.White)
.fillMaxWidth()
.height(200.dp),
contentAlignment = Alignment.Center
) {
Text(
"Text $it",
fontSize = 40.sp,
color = Color.Gray
)
}
}
}
Need to convert from
listState.animateScrollToItem()
to
listState.animateScrollBy()
for smooth indicator change and moving with offset change from Pager.
and do some more methodical scale and color and offset calculating