Creating Custom Paging for Infinite Scrolling in Jetpack Compose
Infinite scrolling is a common feature in modern mobile applications, allowing users to continuously scroll through a list of items without needing to navigate between pages. This article demonstrates how to implement custom paging for infinite scrolling in Jetpack Compose using a LazyColumn
and a footer with a progress indicator.
Composable Function: CustomPagingColumn
Here’s the full code of the composable function we’ll break down:
@Composable
fun CustomPagingColumn(
referralList: List<MarketingReferralsModel.CryptData.Item>,
perPage: Int,
isLoading: Boolean,
onLoadMoreItem: () -> Unit
) {
var previousItemCount by remember { mutableStateOf(perPage) }
val scrollState = rememberLazyListState()
// Observe the scroll position
LaunchedEffect(scrollState) {
snapshotFlow { scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 }
.collect { lastVisibleIndex ->
val totalItems = scrollState.layoutInfo.totalItemsCount
if (totalItems - lastVisibleIndex <= 2 && totalItems >= previousItemCount) {
previousItemCount = totalItems
onLoadMoreItem()
}
}
}
LazyColumn(state = scrollState) {
// Items
items(referralList) {
ReferralsItem(it)
}
// Footer
if (isLoading)
item {
Box(
modifier = Modifier.fillMaxWidth().padding(top = 20.dp),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp), color = AppInfoManager.getAccentColor()
)
}
}
}
}
Breakdown of the Code
fun CustomPagingColumn(
referralList: List<MarketingReferralsModel.CryptData.Item>,
perPage: Int,
isLoading: Boolean,
onLoadMoreItem: () -> Unit
)
Function Parameters
referralList
: The list of items to display.perPage
: The number of items to load per page.isLoading
: A boolean indicating whether more items are currently being loaded.onLoadMoreItem
: A lambda function that is triggered to load more items when the user scrolls to the end of the list.
State Variables
var previousItemCount by remember { mutableStateOf(perPage) }
val scrollState = rememberLazyListState()
previousItemCount
: Keeps track of the number of items loaded previously. Initialized toperPage
.scrollState
: Holds the state of theLazyColumn
, including information about currently visible items.
Infinite Scrolling Logic
LaunchedEffect(scrollState) {
snapshotFlow { scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0 }
.collect { lastVisibleIndex ->
val totalItems = scrollState.layoutInfo.totalItemsCount
if (totalItems - lastVisibleIndex <= 2 && totalItems >= previousItemCount) {
previousItemCount = totalItems
onLoadMoreItem()
}
}
}
LaunchedEffect
: A side-effect that runs a block of code in a coroutine whenscrollState
changes.snapshotFlow
: Converts the current state ofscrollState
to a flow.- collect: Observes changes to the last visible item index.
- Logic: When the user scrolls near the end of the list (
totalItems - lastVisibleIndex <= 2
), it triggersonLoadMoreItem()
to load more items.
Using the CustomPagingColumn
Composable
Here’s how you can use the CustomPagingColumn
composable in your application, including state management for infinite scrolling:
@Composable
fun ReferralsScreen(component: ReferralsComponent) {
var isRefreshing by remember { mutableStateOf(false) }
var dataPage by remember { mutableStateOf(1) }
var pageCount by remember { mutableStateOf(PER_PAGE) }
var isDataRichesEnd by remember { mutableStateOf(false) }
val referralsList = remember { mutableStateListOf<MarketingReferralsModel.CryptData.Item>() }
LaunchedEffect(key1 = dataPage) {
if (!isDataRichesEnd) {
component.marketingViewModel.marketingReferrals(dataPage)
}
}
component.marketingViewModel.marketingReferrals.collectAsState().value.let { loadable ->
isRefreshing = loadable == Loadable.Loading
if (loadable is Loadable.Success) {
referralsList.addAll(loadable.data.cryptData.items)
pageCount = loadable.data.cryptData.count
isDataRichesEnd = loadable.data.cryptData.items.isEmpty()
component.marketingViewModel.resetMarketingReferrals()
}
}
Column(
modifier = Modifier
.fillMaxSize()
.background(AppColors().grayFBFBFB)
) {
CustomPagingColumn(
referralList = referralsList,
perPage = pageCount,
isLoading = isRefreshing,
onLoadMoreItem = {
if (!isRefreshing) dataPage++
}
)
}
}
Explanation
- State Management:
var isRefreshing by remember { mutableStateOf(false) }
var dataPage by remember { mutableStateOf(1) }
var pageCount by remember { mutableStateOf(PER_PAGE) }
var isDataRichesEnd by remember { mutableStateOf(false) }
val referralsList = remember { mutableStateListOf<MarketingReferralsModel.CryptData.Item>() }
isRefreshing
: Indicates whether more items are currently being loaded.dataPage
: Keeps track of the current page.pageCount
: Number of items to load per page.isDataRichesEnd
: Indicates whether there are no more items to load.referralsList
: Holds the list of referral items.
2. Loading Items:
LaunchedEffect(key1 = dataPage) {
if (!isDataRichesEnd) {
component.marketingViewModel.marketingReferrals(dataPage)
}
}
Triggers data loading whenever dataPage
changes, unless isDataRichesEnd
is true.
3. Collecting and Updating State:
component.marketingViewModel.marketingReferrals.collectAsState().value.let { loadable ->
isRefreshing = loadable == Loadable.Loading
if (loadable is Loadable.Success) {
referralsList.addAll(loadable.data.cryptData.items)
pageCount = loadable.data.cryptData.count
isDataRichesEnd = loadable.data.cryptData.items.isEmpty()
component.marketingViewModel.resetMarketingReferrals()
}
}
- Observes the
marketingReferrals
state and updates the local state accordingly. - Adds new items to
referralsList
and updatesisDataRichesEnd
based on whether new items are loaded. - At the end, we reset the
marketingReferrals
state toLoading
in our view model
4. Using CustomPagingColumn
:
CustomPagingColumn(
referralList = referralsList,
perPage = pageCount,
isLoading = isRefreshing,
onLoadMoreItem = {
if (!isRefreshing) dataPage++
}
)
- Passes the current state and data to the
CustomPagingColumn
composable. - Increments
dataPage
to load more items when the user scrolls to the end of the list.
By understanding and utilizing these components, you can create a smooth infinite scrolling experience in your Jetpack Compose applications.