Jetpack Compose: Building Onboarding Screens

Onboarding screens are an essential part of any mobile application. They provide users with a brief introduction to the app’s features, benefits, and how to get started. In this comprehensive guide, we’ll explore how to build beautiful and effective onboarding screens using Jetpack Compose, Android’s modern UI toolkit.

What are Onboarding Screens?

Onboarding screens are a series of UI elements displayed to users when they launch an app for the first time. They serve as a tutorial, guiding users through key functionalities and helping them understand the app’s value proposition.

Why are Onboarding Screens Important?

  • First Impressions: Creates a positive first experience for new users.
  • Feature Discovery: Highlights key features and benefits.
  • User Retention: Reduces user churn by providing a smooth initial experience.
  • User Education: Educates users on how to effectively use the app.

How to Build Onboarding Screens in Jetpack Compose

Here’s a step-by-step guide on creating onboarding screens using Jetpack Compose.

Step 1: Set Up a New Project

Create a new Android project with Jetpack Compose support or use an existing Compose-based project.

Step 2: Add Dependencies

Ensure that you have the necessary dependencies in your build.gradle file.

dependencies {
    implementation("androidx.compose.ui:ui:1.6.4")
    implementation("androidx.compose.material:material:1.6.4")
    implementation("androidx.compose.ui:ui-tooling-preview:1.6.4")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
    implementation("androidx.activity:activity-compose:1.8.2")
    implementation("androidx.compose.material3:material3:1.3.0-alpha02")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.6.4")
    debugImplementation("androidx.compose.ui:ui-tooling:1.6.4")
    debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.4")
    implementation("androidx.compose.pager:pager-accompanist:0.32.0")  // For ViewPager
    implementation("io.coil-kt:coil-compose:2.4.0") // For image loading
}

Step 3: Create Data Class for Onboarding Screens

Define a data class to represent the data for each onboarding screen.

data class OnboardingPage(
    val title: String,
    val description: String,
    val image: Int
)

Step 4: Implement the Onboarding Screens

Create a Composable function to represent each onboarding screen.


import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

@Composable
fun OnboardingScreen(page: OnboardingPage) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Image(
            painter = painterResource(id = page.image),
            contentDescription = null,
            modifier = Modifier.size(200.dp)
        )
        Spacer(modifier = Modifier.height(32.dp))
        Text(
            text = page.title,
            fontSize = 24.sp,
            fontWeight = FontWeight.Bold,
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.h5
        )
        Spacer(modifier = Modifier.height(16.dp))
        Text(
            text = page.description,
            fontSize = 16.sp,
            textAlign = TextAlign.Center,
            style = MaterialTheme.typography.body1
        )
    }
}

Step 5: Use Accompanist Pager to Handle Swiping

To enable swiping between onboarding screens, use Accompanist Pager.


import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.HorizontalPagerIndicator
import com.google.accompanist.pager.rememberPagerState
import kotlinx.coroutines.launch

@OptIn(ExperimentalPagerApi::class)
@Composable
fun OnboardingPager(pages: List, onGetStartedClick: () -> Unit) {
    val pagerState = rememberPagerState()
    val coroutineScope = rememberCoroutineScope()

    Column(modifier = Modifier.fillMaxSize()) {
        HorizontalPager(
            count = pages.size,
            state = pagerState,
            modifier = Modifier.weight(1f)
        ) { page ->
            OnboardingScreen(page = pages[page])
        }

        HorizontalPagerIndicator(
            pagerState = pagerState,
            modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .padding(16.dp),
        )

        Button(
            onClick = {
                if (pagerState.currentPage < pages.size - 1) {
                    coroutineScope.launch {
                        pagerState.animateScrollToPage(pagerState.currentPage + 1)
                    }
                } else {
                    onGetStartedClick()
                }
            },
            modifier = Modifier
                .align(Alignment.CenterHorizontally)
                .padding(16.dp)
        ) {
            Text(text = if (pagerState.currentPage == pages.size - 1) "Get Started" else "Next")
        }
    }
}

Step 6: Create Mock Data and Usage

Now, you can display these onboarding screens within your app by preparing some mock data.


import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember

@Composable
fun MainContent() {
    val showOnboarding = remember { mutableStateOf(true) }

    val onboardingPages = listOf(
        OnboardingPage(
            title = "Welcome to Awesome App",
            description = "Discover amazing features and get started with ease.",
            image = androidx.core.R.drawable.ic_call_answer
        ),
        OnboardingPage(
            title = "Explore New Features",
            description = "Learn about the powerful tools available to you.",
            image = androidx.core.R.drawable.ic_call_decline
        ),
        OnboardingPage(
            title = "Get Ready to Go!",
            description = "Ready to start using the app? Let's get started!",
            image = androidx.core.R.drawable.ic_call_end
        )
    )

    if (showOnboarding.value) {
        OnboardingPager(
            pages = onboardingPages,
            onGetStartedClick = { showOnboarding.value = false }
        )
    } else {
        Text("Welcome to the app!")
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MaterialTheme {
        MainContent()
    }
}

Enhancements and Best Practices

  • Image Loading: Use Coil or Glide for efficient image loading.
  • Animations: Add animations for a more engaging experience.
  • Accessibility: Ensure screens are accessible with proper descriptions and screen reader support.
  • Theming: Follow your app’s theme for a consistent look and feel.

Conclusion

Jetpack Compose provides a straightforward and efficient way to create onboarding screens that enhance user experience. By leveraging components like HorizontalPager and designing visually appealing screens, you can effectively guide users through your app’s features and increase user retention.