ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

ViewPager in Jetpack Compose using Official API

--

This story will show how to create ViewPager (HorizontalPager /VerticalPager ) in Jetpack Compose.

Photo by Amélie Mourichon on Unsplash

I have written another story about Tabs Layout in Jetpack Compose using Official API. You can read it there.

ViewPager is a common design layout used in android apps. In the story we will see how to create a ViewPager using Jetpack Compose. In Jetpack compose they are called HorizontalPager and VerticalPager .

Content

High level overview of the page content

  • Dependency
  • Horizontal Pager (Simple Example)
  • Horizontal Pager with Next and Prev Button (Manual Scrolling)
  • Horizontal Pager with Images and Dot Indicators
  • Vertical Pager with Images
  • Github Project

Dependency

HorizontalPager and VerticalPager API were initially part of accompanist But from Compose 1.4.0+ it's part of official API, But still they are in Experimental phase. You need to add Compose Foundation dependency inside the project. You can see the latest version here

implementation 'androidx.compose.foundation:foundation:1.4.1'

Your project might give errors because you might still be on an older version of Compose Compiler. You need to upgrade your Compose Compiler version to at least 1.4.0 and for that you might also need to update your Kotlin version. You can see Kotlin version to Compose Compiler version mapping here. I am using Kotlin version Compiler version 1.8.10 and Compose Compiler version 1.4.4

You should update Compose Compiler version in gradle files as below

composeOptions {
kotlinCompilerExtensionVersion '1.4.4'
}

Horizontal Pager Composable

Lets look at HorizontalPager composable and parameters required by the composable

Explaining parameters which we will use in our examples below.

  • pageCount — thats number of pages we want to show
  • pageSize — it represents size of the pager itself (not the content size), default value is PagerSize.Fill that means for HorizontalPager it will take complete width of the screen and for VerticalPager it will take complete height of the screen as default size of the pager.
  • pageSpacing — it represents the distance between two pagers of HorizontalPager or VerticalPager
  • state — it keeps the pager state i.e which page user is currently visible and provides the scrolling functionality
  • pageContent —it provides composable lambda which will represent actual content of the pager. lambda provides the page index of the page which is currently being displayed on screen which will be used to disable the custom page inside HorizontalPager
  • modifier — that’s generic modifier parameter as every composable function has.

Horizontal Pager (Simple Example)

Lets see basic example of HorizontalPager . In example below we are creating pager state using rememberPagerState() and passing into HorizontalPager along with number of pages we want to show which is 5. pagerState manages scrolling state of the HorizontalPager , keept track of current page the pager is at and it can be used to scroll to a specific page manually, we will see its usage later in next example.

Output:

Horizontal Pager with Next and Prev Button (Manual Scrolling)

In the next example we are adding Next and Prev Buttons on the pager to show usage of rememberPagerState , which you can use to manually scroll to a specific page as shown in example below

We are using Box to lay Next and Prev buttons on top of HorizontalPager

derivedStateOf Api is used to determine when to enable Next and Prev Buttons. derivedStateOf is an ideal choice here because we don’t want to recompose buttons every time page index changes in order to avoid unnecessary recompositions. see below code for prevButtonVisible

val prevButtonVisible = remember {
derivedStateOf {
pagerState.currentPage > 0
}
}

Prev Button will be visible when pagerState.currentPage index is greater than 0, but we don’t want to recompose Prev Button when pagerState.currentPag is 2,3 or 4 so, derivedStateOf will handle it automatically i.e it enables Prev Button when current page index is 1 and it will not recompose Prev button if pagerState.currentPage index is 2,3 and 4 to avoid unnecessary recompositions.

pagerState provides property pagerState.currentPage which tells current visible page index, and a method animateScrollToPage which scrolls the pager to provided pageIndex.

scrollToPage is also another method provided on pagerState which can be used if we don’t need animation during scroll.

animateScrollToPage is also a suspend function, so calling a suspend outside of composable we need coroutineScope to launch animateScrollToPage , that’s why we created coroutine scope using api rememberCoroutineScope

rememberCoroutineScope is a composable which is called to get a coroutine scope. It's tied to the composable function from where it's called and gets canceled when that composable does not exist. i.e you can launch coroutines using this scope without worrying about lifecycle of coroutines.

Output:

Horizontal Pager with Images and Dot Indicators

In the next example we want to show images inside HorizontalPager with the dot indicators at the bottom. For the dot indicators we will use HorizontalPagerIndicator Api from accompanist . HorizontalPagerIndicator Api is still not moved to official compose dependencies that’s why we have to use from accompanist and it’s compatible with HorizontalPager from Jetpack compose foundation dependency.

Add the following dependency for HorizontalPagerIndicator , see the compatible version in the Readme section of the github repo of accompanist.

implementation "com.google.accompanist:accompanist-pager-indicators:0.30.1"

Lets look at the final code below

HorizontalPagerIndicator composable is taking pageCount and pagerState

pageCount must be same as we are passing in HorizontalPager which is 5 also pagerState is taking the same reference being pass in HorizontalPager created via rememberPagerState() so both HorizontalPager and HorizontalPagerIndicator must have the same pager state passed to them in order to sync with each other.

HorizontalPager is showing an image per page which is being accessed from the list of images using the index of the current page.

Box is used to overlay HorizontalPagerIndicator on top of HorizontalPager

Output:

Vertical Pager with Images

We have seen some examples of HorizontalPager in the story above. Let's now see VerticalPager. We will use a list of images to display in VerticalPager . The code below.

VerticalPager is using the following extra parameters which we didn't use in HorizontalPager even though they also existed in HorizontalPager .

  • pageSize — it represents the size of page to be use, we are using Fixed 300.dp in our code that means on screen we will see multiple pages depending upon how many can appear on any device size
  • pageSpacing — it handles the spacing between two pages

The output of the code below will clarify the effect of these properties.

Migration from Accompanist to Compose Foundation API

Before this api was officially introduced in Compose Foundation dependency, it was available in accompanist dependency. If you have already used from accompanist and you want to move to official dependency then there is migration guide available here

Thats it for now, Hope it was helpful 🚀

Sources

Photo Credits/Attributes

All Images used in this coding project are taken from www.freepik.com and their photo credit/attributes are mentioned below.

Github Project

Remember to follow and 👏 if you liked it :)

— — — — — — — — — — —

GitHub | LinkedIn | Twitter

--

--

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Saqib

Senior Mobile Engineer (Android & iOS) , Berlin | Sharing my development experience

Responses (2)