Android + Firestore + Flow + Pagination = ❤

Torkel Velure
ProAndroidDev
Published in
3 min readFeb 12, 2021

--

I’ve been working on this app recently: Fjelltoppjakten

In Fjelltoppjakten we use Firebase/Firestore for everything!

The app is simple; you can view all the mountains in Bergen(Norway), and if you reach the summit, you can check in and gain points!

After gaining hundreds of users and 50+ check-ins daily, it wasn’t feasible anymore to fetch all users and check ins immediately, nor was it a good solution to only fetch/show a few of them.

We had to implement some kind of pagination.

Our previous implementation used a callback flow and a snapshot listener:

It returns a flow of a SnapshotListener for a query, works great if you just want to listen to few documents!

And to get all the top users we would do something like this this:

Given that we have thousands of potential documents, we don’t want to read them all at once, that could be costly. We want to paginate the query!

In addition to adding pagination, we don’t want to change the other code too much, our repository should still return a flow, keeping our functional approach!

By reading the firebase documentation we can see that they recommend using query cursors, namely startAt and startAfter.

Adding pagination

First we’ll listen to our recycler view and add the last visible item index to a MutableStateFlow.

In our viewModel we add:

val lastVisibleItem = MutableStateFlow<Int>(0)

And in our fragment we add (feel free to make a binding adapter instead!):

The above code updates lastVisibleItem whenever the last visible item index in the recycler view is larger than the last seen one. (Simply, it will be incremented until the user has scrolled to the bottom of the recycler view)

Using the lastVisibleItem flow, we could rewrite our flow like this:

Here we immediately fetch the top 25 users and store them in a mutable list, then we map them to local entities and emit them. Further we listen to lastVisibleItem with a transform, and whenever it is updated we check if it is equal to the size of the list of documents we currently have, and if it is, we fetch the next 25 users by using startAfter with the last document we previously fetched. We then add the newly fetched documents to our list and emit the whole list of documents.

This works perfectly! However, we have many queries, and we don’t want to write that much code every time..

We can write a generic pagination function like this:

And then our paginated users query will be as simple as:

And in our viewModel we just write:

Now leaders is a paginated flow that will update/grow whenever the user scrolls to the bottom of our recycler view. We just bind the leaders flow to our recycler view, and there is nothing more to worry about!

Now we can update all our other queries as well. Just add a lastVisibleItem flow and add .paginate(lastVisibleItem) to our queries instead of .limit(x) 👏

While using this pagination implementation we won’t get real-time updates like we did with the snapshotlistener, but that’s a trade-off we can live with.

--

--