How to create Tabs with Jetpack Compose

Note: You need to have Android Studio Arctic Fox and above to use Jetpack Compose in your project.

In this tutorial, we’re going to see how to create Tabs with Jetpack Compose and be able to scroll between them using the Pager from the group of libraries Accompanist.

Adding libraries

Go to your project-level gradle.build file, and add the following extension:

buildscript { ext { compose_version = '1.0.0-rc02' } // ... }
Code language: Kotlin (kotlin)

Now, go to your app-level gradle.build file, and add the following:

android { // ... kotlinOptions { jvmTarget = '1.8' useIR = true } buildFeatures { // ... compose true } composeOptions { kotlinCompilerExtensionVersion compose_version } } dependencies { // ... implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.ui:ui-tooling:$compose_version" implementation "androidx.navigation:navigation-compose:2.4.0-alpha04" implementation "androidx.activity:activity-compose:1.3.0-rc02" // Accompanist implementation "com.google.accompanist:accompanist-pager:0.14.0" // Pager implementation "com.google.accompanist:accompanist-pager-indicators:0.14.0" // Pager Indicators // ... }
Code language: Kotlin (kotlin)

Creating the Views (Screens)

Before we start with the Tabs, let’s create the Views (or Screens) that we’re going to display when we select a tab.

In this example, I have created a Kotlin file named ContentScreens, and added three different Screens that show the name of the view in Text in the middle of the screen.

@Composable fun MusicScreen() { Column( modifier = Modifier .fillMaxSize() .background(colorResource(id = R.color.colorPrimary)) .wrapContentSize(Alignment.Center) ) { Text( text = "Music View", fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier.align(Alignment.CenterHorizontally), textAlign = TextAlign.Center, fontSize = 25.sp ) } } @Preview(showBackground = true) @Composable fun MusicScreenPreview() { MusicScreen() } @Composable fun MoviesScreen() { Column( modifier = Modifier .fillMaxSize() .background(colorResource(id = R.color.colorPrimary)) .wrapContentSize(Alignment.Center) ) { Text( text = "Movies View", fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier.align(Alignment.CenterHorizontally), textAlign = TextAlign.Center, fontSize = 25.sp ) } } @Preview(showBackground = true) @Composable fun MoviesScreenPreview() { MoviesScreen() } @Composable fun BooksScreen() { Column( modifier = Modifier .fillMaxSize() .background(colorResource(id = R.color.colorPrimary)) .wrapContentSize(Alignment.Center) ) { Text( text = "Books View", fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier.align(Alignment.CenterHorizontally), textAlign = TextAlign.Center, fontSize = 25.sp ) } } @Preview(showBackground = true) @Composable fun BooksScreenPreview() { BooksScreen() }
Code language: Kotlin (kotlin)

Creating the Tab Items

Next, we’re going to create a new file that contains everything for each tab item (icon, title, and the screen that we’re going to display)

Create a new sealed class file by going to your project folder > Right-click > New > Kotlin Class/File

On the new window, select Sealed Class and give TabItem as a name

To be able to retrieve a View (Screen) from a list (you’ll see what I’m talking about later) as @Composable and not as Unit type, we create a typealias that changes the Unit to @Composable

typealias ComposableFun = @Composable () -> Unit sealed class TabItem(var icon: Int, var title: String, var screen: ComposableFun) { object Music : TabItem(R.drawable.ic_music, "Music", { MusicScreen() }) object Movies : TabItem(R.drawable.ic_movie, "Movies", { MoviesScreen() }) object Books : TabItem(R.drawable.ic_book, "Books", { BooksScreen() }) }
Code language: Kotlin (kotlin)

Creating the View (Screen) for the Tabs

Now, let’s create the View(Screen) that will contain the Tabs and the Views

Create a new composable function and give it the name MainScreen.

Inside this function, we create a list for the tabs, a variable that saves the state of the pager, and we’re setting up the Scaffold layout.

class MainActivity : ComponentActivity() { @ExperimentalPagerApi @ExperimentalMaterialApi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MainScreen() } } } @ExperimentalPagerApi @ExperimentalMaterialApi @Composable fun MainScreen() { val tabs = listOf( TabItem.Music, TabItem.Movies, TabItem.Books ) val pagerState = rememberPagerState(pageCount = tabs.size) Scaffold( topBar = { /* Add code later }, ) { /* Add code later */ } } @ExperimentalPagerApi @ExperimentalMaterialApi @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() }
Code language: Kotlin (kotlin)

Creating the Top Bar

In the same file, add the following composable function to create the Top Bar

@Composable fun TopBar() { TopAppBar( title = { Text(text = stringResource(R.string.app_name), fontSize = 18.sp) }, backgroundColor = colorResource(id = R.color.colorPrimary), contentColor = Color.White ) } @Preview(showBackground = true) @Composable fun TopBarPreview() { TopBar() }
Code language: Kotlin (kotlin)

Creating the Tabs

Create a new composable function for the Tabs with two parameters, the list of tabs items and the pager’s state.

@ExperimentalPagerApi @ExperimentalMaterialApi @Composable fun Tabs(tabs: List<TabItem>, pagerState: PagerState) { val scope = rememberCoroutineScope() // OR ScrollableTabRow() TabRow( selectedTabIndex = pagerState.currentPage, backgroundColor = colorResource(id = R.color.colorPrimaryDark), contentColor = Color.White, indicator = { tabPositions -> TabRowDefaults.Indicator( Modifier.pagerTabIndicatorOffset(pagerState, tabPositions) ) }) { tabs.forEachIndexed { index, tab -> // OR Tab() LeadingIconTab( icon = { Icon(painter = painterResource(id = tab.icon), contentDescription = "") }, text = { Text(tab.title) }, selected = pagerState.currentPage == index, onClick = { scope.launch { pagerState.animateScrollToPage(index) } }, ) } } } @ExperimentalMaterialApi @ExperimentalPagerApi @Preview(showBackground = true) @Composable fun TabsPreview() { val tabs = listOf( TabItem.Music, TabItem.Movies, TabItem.Books ) val pagerState = rememberPagerState(pageCount = tabs.size) Tabs(tabs = tabs, pagerState = pagerState) }
Code language: Kotlin (kotlin)

Setting up the Views for the Tabs

In the same file again, create a new composable function TabsContent with the same two parameters as previously, the list of tabs items and the pager’s state.

In this function, we have a HorizontalPager that takes the state of the pager, and we’re displaying the View (or Screen) of the tab that is selected.

@ExperimentalPagerApi @Composable fun TabsContent(tabs: List<TabItem>, pagerState: PagerState) { HorizontalPager(state = pagerState) { page -> tabs[page].screen() } } @ExperimentalMaterialApi @ExperimentalPagerApi @Preview(showBackground = true) @Composable fun TabsContentPreview() { val tabs = listOf( TabItem.Music, TabItem.Movies, TabItem.Books ) val pagerState = rememberPagerState(pageCount = tabs.size) TabsContent(tabs = tabs, pagerState = pagerState) }
Code language: Kotlin (kotlin)

Connecting the TopBar, Tabs and TabsContent Views

Now, in the final step, it’s time to add all the views together.

Go back to the MainScreen, add the TopBar to the Scaffold’s layout topBar modifier, and create a new Column inside the brackets.

Inside this Column, add the Tabs and the TabsContent and pass the arguments tabs and pagerState.

@ExperimentalPagerApi @ExperimentalMaterialApi @Composable fun MainScreen() { val tabs = listOf(TabItem.Music, TabItem.Movies, TabItem.Books) val pagerState = rememberPagerState(pageCount = tabs.size) Scaffold( topBar = { TopBar() }, ) { Column { Tabs(tabs = tabs, pagerState = pagerState) TabsContent(tabs = tabs, pagerState = pagerState) } } } @ExperimentalPagerApi @ExperimentalMaterialApi @Preview(showBackground = true) @Composable fun MainScreenPreview() { MainScreen() }
Code language: Kotlin (kotlin)
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments

Get once a week my latest tutorials right in your inbox

Check your inbox to confirm your email