Table of contents
My app on the Google Playstore
GitHub code
YouTube Version
Introduction
- This series will be an informal demonstration of any problems I face or any observations I make while developing Android apps. Each blog post in this series will be unique and separate from the others, so feel free to look around.
What we are talking about
- In this tutorial we will be implementing a simple but hacky endless scrolling in jetpack compose.
Getting started
- here are really two scenarios we have to deal with:
1) The user is always shown data (like TikTok)
2) There is a chance the user can see no data
1) The user is always shown displayed data
- This is the easiest of the two, We can just create a simple LazyColumn and use its DSL language: ```
@Composable
fun EndlessScrolling(paddingValues: PaddingValues){
val notesList = remember {
mutableStateListOf("2","2","3","2","2","3",)
}
Box() {
LazyColumn(
state= state,
modifier= Modifier
.fillMaxSize()
.padding(bottom = scaffoldPaddingBottom)
) {
items(notesList) { item ->
Card(
modifier = Modifier
.fillMaxWidth()
.height(itemHeight),
elevation = 8.dp
){
Text(text = "Item $item")
}
}
item{
CircularProgressIndicator( color = Color.Black)
LaunchedEffect(true){
notesList.add("3")
notesList.add("3")
notesList.add("3")
notesList.add("3")
notesList.add("3")
}
}
}
}
}
- As you can see the LazyColumn first uses `items(notesList) {}` to show all of the items inside of the list. Then once all of the items have been loaded and scrolled through, `item{}` is reached and we run the LaunchedEffect(true){} composable to add more items to the list.
- Now in a real world scenario, we can remove the LaunchEffect composable and just have this inside of `item{}`:
item{
//this would be the method that makes a call to get a new
// batch of data from the database and add it to the state,
// which is stored in the viewModel
viewModel.makeNetworkRequest()
}
- We are only able to remove `LaunchedEffect` if the composable is [stateless](https://developer.android.com/jetpack/compose/state).
#### The problems with the item{} block
- The code in the previous code blocks only works properly if there are enough items to cover the entire screen and the user has to scroll to see them all. This becomes a problem if there are no items or not enough items to enable scrolling in the LazyColumn. Which case the `item{}` block will run continuously
###2) There is a chance the user can see no data <a name="two"></a>
- This is where things start to get a little hacky. The code is very specific to my project, mainly because I have used a [Scaffold](https://developer.android.com/jetpack/compose/layouts/material#scaffold) with a bottom bar and a top bar and I do a little bit of calculations based on that fact. So if your code does not contain a scaffold it will be a little different
- To better explain what we are trying to do I have created this diagram. Please read on and refer back to it:
![layout diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7r54q0dcr4wpfkwsc4g6.png)
- Within this hacky solution there are two steps we must follow:
1) Determine the height of the LazyColumn visible to the user.
2) determine if the LazyColumn item's combined height exceeds the height of the LazyColumn visible to the user
- All of this is done with this code:
@Composable
fun RefreshingAnimation(paddingValues: PaddingValues){
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp // total device screen height
val scaffoldBottomBarHeight = paddingValues.calculateBottomPadding() // bottom bar height
val scaffoldTopBarHeight = 100.dp // top bar height, found through trial and error
val screenHiddenByBars = scaffoldBottomBarHeight + scaffoldTopBarHeight // combination of bottom and top bar
val screenVisibleToUser = screenHeight - screenHiddenByBars.value //lazyColumn height visible to user
val notesList = remember {
mutableStateListOf("2","2","2","2")
}
val totalItemHeight = notesList.size* itemHeight.value
val userCanScroll = (totalItemHeight > screenVisibleToUser)
}
- Then similar to the first scenario we can use the `item{}` block to determine if the user is at the bottom of the LazyColumn. However, we use the use the `userCanScroll` variable to determine if there are enough items to make the user scroll:
item{
CircularProgressIndicator( color = Color.Black)
LaunchedEffect(true){
if (userCanScroll){
delay(2000)
notesList.add("3")
notesList.add("3")
notesList.add("3")
notesList.add("3")
notesList.add("3")
}
}
}
- The `userCanScroll` variable is used as a conditional to tell our code if the user had to scroll to get to the bottom of the LazyColumn. If this is true that means we can run our code and make a network request.
### Whats next? <a name="next"></a>
- In Part 2 we will be implementing a FireBase database and making paginated requests.
### Conclusion
- Thank you for taking the time out of your day to read this blog post of mine. If you have any questions or concerns please comment below or reach out to me on [Twitter](https://twitter.com/AndroidTristan).
Top comments (0)