As of writing this article, Jetpack Compose is in beta! Yey! However, it is not totally complete. There is not (AFAIK) yet an official view pager library from the Compose team. But, there are some options.
In Sats we are trying out Jetpack Compose and was faced with the issue of implementing a view pager in our app. Together with the team we wanted to implement sort of like a news flashing card in the top of the app, showing your three first upcoming workout sessions. The next vertical card was going to show the three closest clubs if the user had granted us the permission to track their location. This implied having a lot of different states that the cards could be in, as you could have zero, one, two or three upcoming sessions. In addition to that we also had to implement the possibility for having a upcoming PT session in the mix. The nearby clubs card could also have a bunch of different states as shown in the design illustration below.
So, IMO this was a perfect candidate for Jetpack Compose since then we did not have to deal with writing a hole bunch of XML markup for each state and so fourth. So I thought; this is going to be a lot of @Composable fun! And I was right!
Firstly we started looking into the Jetcaster app, where they have implemented a view pager in Compose. We used this article by Jorge Castillo, which guides you through every part of the implementation. However, we noticed some things we did not like with this implementation. If you try it out, you will find it a bit strange to use because of issues with the fling and touch handling. So if you want to become the next Compose rockstar, you should definitely start writing a pager library for Jetpack Compose! But wait …
So what are the options?
Right now, Chris Banes is figuring out something by using the Jetcaster solution and making it more pleasant for the user to use. You can follow this PR for more information. Meanwhile, a solution is to use Compose Android view interop. Then we would still take advantage of all the powerful features in the viewpager2 library, but also be able to use all the mighty features of Jetpack Compose!
Firstly, what do I mean about Compose Android veiw interop?
Compose Android view interop, is essentially the way for us to combine Android XML and composable functions. There are a couple of ways to do this. You can either inject an Android view into a composable (you can read more about that here) or you can inject a composable into an Android view. So in Sats, in order for us to use viewpager2, we have to go with the seccond approach.
But how?
Firstly, we started by making a custom Android view and injecting it into your XML code like this:
(Our discover fragment is the first fragment the user sees in our app and is displayed with our new “hot” information about Sats and the app.)
Furthermore, in our brand new custom Framelayout, we inflated our view pager layout. Then we set our adapter, orientation and offscreenPageLimit. The offScreenPagelimit is how many pages that will be kept offscreen on either side of the pager.
Since we are going to work with Compose, it makes sense to use unidirectional data flow. You can read more about that here if you are not familiar with this type of achitecture.
Further, we needed a view model to hold our states and fetch our data.
As you can se here, the position object is nullable here. This will be null if the user has not granted permission in the fragment to use their location (or the location is null for some reason).
We then added an observer on that StateFlow in our fragment.
As you can se from the lambda above, we are calling on the setViewState function in our custom composable. Since the StateFlow in the view model is initialized with TopStoryViewState(Lce.Loading), it will start in the loading state. In the custom frame layout, we will set the state and update our viewpager2 adapter according to our state.
As a sidenote, you can see that we can easily use Compose in here as we did with the page indicator. We can use a Compose xml view and override the set content method with our composable function.
The viewpager2 adapter
Our view holders
So, as you can see there is a lot going on in this adapter and we are going to look into it in a bit more detail. Firstly, we need to know which view holder to inflate. For that, we made a companion object that will hold our view type and return that when we know what state is being held. Then, when the view holder is being created it can inflate the proper view holder determined by the top story state. In our view holders we are pasing in a ComposeView that we can invoke the setContent(@Composable () -> Unit) function on. This is essentially making our bridge from Android view to Compose.
Furthermore, for the view holders that actually display data that can be interacted with, like retry buttons, book buttons and other data that needs us to update our view effect so that we can navigate places. They need to pass in a callback interface, so that we in our fragment can interact with that and our view model. This will handle our unidirectional data flow going up from the view.
Conclusion
Eventhough Jetpack Compose is just in beta, IMO it works really well! There is no problem combining Android views and Compose views with interop. This makes Compose really flexible and easy to use in apps that are not already completely written in Compose. Further, if you need to use a viewpager in Jetpack Compose, IMO this is not a bad solution until the Compose team comes out with their own official version.