DEV Community

Cover image for Proof of concept. Endless scrolling in Android with Jetpack Compose. Part 1
Tristan Elliott
Tristan Elliott

Posted on

Proof of concept. Endless scrolling in Android with Jetpack Compose. Part 1

Table of contents

  1. What we are talking about
  2. Getting started
  3. Scenario 1
  4. Scenario 2
  5. Whats next?

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<String>("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")

                }
            }

        }


    }


}

Enter fullscreen mode Exit fullscreen mode
  • 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()

         }

Enter fullscreen mode Exit fullscreen mode
  • We are only able to remove LaunchedEffect if the composable is stateless.

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

  • 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 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

  • 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<String>("2","2","2","2")
    }

    val totalItemHeight = notesList.size* itemHeight.value

    val userCanScroll = (totalItemHeight > screenVisibleToUser)

}
Enter fullscreen mode Exit fullscreen mode
  • 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")
                    }

                }
            }

Enter fullscreen mode Exit fullscreen mode
  • 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?

  • 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.

Top comments (0)