Skip to content

Jetpack Compose Pager — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about Jetpack Compose Pager. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

The Problem

Your horizontal pager scrolls through multiple pages in one swipe, the page indicator doesn't match, or the pager doesn't snap to full pages.

Wrong Approach ❌

// Using ViewPager XML inside Compose — fighting the framework
AndroidView(factory = { ViewPager(it) }) { /* adapter */ }

@Composable
fun BadPager() {
    // Scrollable row — doesn't snap to pages
    Row(horizontalScroll(rememberScrollState())) {
        items(5) { Page(it) }
    }
}

Output: No snapping. Multiple pages visible at once.

Right Approach ✅

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GoodPager() {
    val pagerState = rememberPagerState(
        initialPage = 0,
        pageCount = { 5 }
    )

    HorizontalPager(
        state = pagerState,
        modifier = Modifier.fillMaxSize(),
        pageSpacing = 16.dp,
        // Custom snapping
        beyondViewportPageCount = 1
    ) { page ->
        Card(
            modifier = Modifier.fillMaxSize().padding(16.dp),
            colors = CardDefaults.cardColors(containerColor = colors[page])
        ) {
            Text("Page ${page + 1}", modifier = Modifier.align(Alignment.Center))
        }
    }

    // Page indicator
    HorizontalPagerIndicator(
        pagerState = pagerState,
        modifier = Modifier.align(Alignment.BottomCenter).padding(16.dp)
    )
}

// Vertical pager
VerticalPager(state = rememberPagerState(pageCount = { 10 })) { page ->
    Text("Vertical page $page")
}

Output: Snapping horizontal pager with matching indicator.

Prevention

  • Use HorizontalPager from foundation.pager (not accompanist-pager).
  • Use rememberPagerState with pageCount lambda.
  • Use HorizontalPagerIndicator for page dots.
  • Use userScrollEnabled = false for non-interactive pagers.

Common Mistakes with compose pager

  1. Mixing let bindings with <- bindings in do notation, producing type errors
  2. Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
  3. Non-exhaustive pattern matches that compile with warnings then crash at runtime

These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

### How do I disable user swiping on a pager?

Set userScrollEnabled = false on HorizontalPager and control page changes programmatically via pagerState.animateScrollToPage().

### What is beyondViewportPageCount?

It controls how many pages are composed off-screen. Higher values reduce jank during fast swipes but use more memory. Default is usually sufficient.

### Can I use nested pagers (vertical inside horizontal)?

Yes, but be careful with gesture conflicts. Use Modifier.verticalScroll for the inner content or handle touch event priorities carefully.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro