7. Paginate results
13m

Overview

Pagination is one area where shines — but it can be a bit tricky at first. Let's take a closer look! 👀

In this lesson, we will:

  • Use the cursor to load new sets of data
  • Implement the app logic to programmatically load more data

Paginating the list of launches

As you might have noticed, the object returned from the LaunchListQuery is a LaunchConnection. This object has a list of , a pagination , and a boolean to indicate whether more launches exist.

schema.graphqls
type LaunchConnection {
cursor: String!
hasMore: Boolean!
launches: [Launch]!
}

When using a -based pagination system, it's important to remember that the cursor gives you a place where you can get all results after a certain spot, regardless of whether more items have been added in the interim.

In an earlier lesson, we hardcoded the SMALL size directly in the , but we can also define arguments programmatically using . We'll use them here to implement pagination.

Add a cursor variable

In LaunchList.graphql, add a cursor . In , variables are prefixed with the dollar sign.

Also add the cursor and hasMore to the , as we will use them to paginate:

app/src/main/graphql/LaunchList.graphql
query LaunchList($cursor: String) {
launches(after: $cursor) {
cursor
hasMore
launches {
id
site
mission {
name
missionPatch(size: SMALL)
}
}
}
}

We can return to the Variables panel in Explorer to test this out. If we omit the $cursor , the server returns data starting from the beginning. Try it out:

Now let's make sure our app's views are making use of this cursor.

Paginate the list of launches

Declare and remember a cursor var, initialized to null, and add it as a key to LaunchedEffect. That way, the will be re-executed every time the cursor changes.

Also keep a reference to response so we can access the hasMore and cursor further down.

app/src/main/kotlin/com/example/rocketreserver/LaunchList.kt
@Composable
fun LaunchList(onLaunchClick: (launchId: String) -> Unit) {
var cursor: String? by remember { mutableStateOf(null) }
var response: ApolloResponse<LaunchListQuery.Data>? by remember { mutableStateOf(null) }
var launchList by remember { mutableStateOf(emptyList<LaunchListQuery.Launch>()) }
LaunchedEffect(cursor) {

Pass the cursor to the LaunchListQuery, and add a special item at the end of the list which updates the cursor if hasNext is true. This will trigger a new with the new whenever the user scrolls to the end of the list, and launchList will be concatenated with the new results.

Note: this is a basic implementation of pagination in Compose. In a production project you may use something more advanced, like the Jetpack Paging library.

The whole function should look like this:

app/src/main/kotlin/com/example/rocketreserver/LaunchList.kt
@Composable
fun LaunchList(onLaunchClick: (launchId: String) -> Unit) {
var cursor: String? by remember { mutableStateOf(null) }
var response: ApolloResponse<LaunchListQuery.Data>? by remember { mutableStateOf(null) }
var launchList by remember { mutableStateOf(emptyList<LaunchListQuery.Launch>()) }
LaunchedEffect(cursor) {
response = apolloClient.query(LaunchListQuery(Optional.present(cursor))).execute()
launchList = launchList + response?.data?.launches?.launches?.filterNotNull().orEmpty()
}
LazyColumn {
items(launchList) { launch ->
LaunchItem(launch = launch, onClick = onLaunchClick)
}
item {
if (response?.data?.launches?.hasMore == true) {
LoadingItem()
cursor = response?.data?.launches?.cursor
}
}
}
}

Note that we wrap the cursor in an Optional: this is because this parameter can be omitted in the .

Test (simplified) pagination

Click Run. You can now see all SpaceX back to their first FalconSat from Kwajalein Atoll!

The simulator showing the end of the list of launches - the last entry is "FalconSat from Kawajalein Atoll"

You might notice that this pagination is happening via a pattern called "infinite scrolling," but many applications choose to show the user page numbers or "tap to load more." You can implement any of these patterns with and .

Task!

Up next

Let's wrap up this course by implementing a notification feature when we book a seat on a . Full steam ahead to !

Previous

Share your questions and comments about this lesson

This course is currently in

beta
. Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.