Exploring Jetpack Compose: Building a Feature Introduction Screen with HorizontalPager

Murad
3 min readDec 25, 2023

--

In this article, we’ll explore how to use HorizontalPager to create a captivating feature introduction screen in your Android app.

Setting the Stage with Dependencies

To get started, ensure you have the necessary dependencies in your build.gradle file:

implementation("androidx.compose.ui:ui:$compose_version")
implementation("androidx.compose.foundation:foundation:$compose_version")
implementation("androidx.compose.material:material:$compose_version")
implementation("androidx.navigation:navigation-compose:$navigation_version")

Replace $compose_version and $navigation_version with the latest versions available.

The GreetingScreen Composable

Our main attraction is the GreetingScreen composable, where the magic happens. This composable leverages HorizontalPager to showcase different greetings to the user, guiding them through the app's features.

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun GreetingScreen(){

val greetings = listOf(
Triple(R.drawable.img_greeting_2,"All your favorites","Get all your loved foods in one once place, you just place the orer we do the rest"),
Triple(R.drawable.img_greeting_4,"All your favorites","Get all your loved foods in one once place, you just place the orer we do the rest"),
Triple(R.drawable.img_greeting_3,"Order from choosen chef","Get all your loved foods in one once place, you just place the orer we do the rest"),
Triple(R.drawable.img_greeting_1,"Free delivery offers","Get all your loved foods in one once place, you just place the orer we do the rest")
)

var buttonName by rememberSaveable { mutableStateOf("Next") }
var visibleSkip by remember { mutableStateOf(true) }

val pagerState = rememberPagerState{greetings.size}
val scope = rememberCoroutineScope()

LaunchedEffect(pagerState) {
snapshotFlow { pagerState.currentPage }.collect { page ->
if (page<3) {
visibleSkip = true
buttonName = "Next"
}else{
visibleSkip = false
buttonName = "Get Started"
}
}
}

Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxSize()
.padding(top = 24.dp, bottom = 16.dp)
) {

HorizontalPager(
modifier = Modifier.weight(1f),
state = pagerState,
key = {greetings[it]},
pageSize = PageSize.Fill
) {
ItemGreeting(greetings[it])
}

Spacer(modifier = Modifier.height(32.dp))

DotsIndicator(
totalDots = greetings.size,
selectedIndex = pagerState.currentPage,
selectedColor = R.color.orange,
unSelectedColor = R.color.orange_light
)

Spacer(modifier = Modifier.height(64.dp))


OrangeButton(
title = buttonName,
modifier = Modifier.padding(start = 24.dp, end = 24.dp)) {
if (pagerState.currentPage ==0 || pagerState.currentPage<3) {
scope.launch {
pagerState.animateScrollToPage(
pagerState.currentPage + 1
)
}
}else {
//start app
}
}

Spacer(modifier = Modifier.height(16.dp))

if(visibleSkip)
Text(text = "Skip",
modifier = Modifier
.height(48.dp)
.padding(horizontal = 12.dp, vertical = 12.dp)
.clickable {
navController?.navigate(Screen.LoginPhone.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
}
},
textAlign = TextAlign.Center,
fontFamily = FontFamily(Font(R.font.sen_regular)),
fontSize = 16.sp,
color = colorResource(id = R.color.blue_dark3)
)
else Spacer(modifier = Modifier.height(48.dp))


}
}

Diving into HorizontalPager

The heart of our feature introduction screen is the HorizontalPager. This powerful composable allows us to display a series of greetings horizontally, providing an engaging user experience.

   HorizontalPager(
modifier = Modifier.weight(1f),
state = pagerState,
key = {greetings[it]},
pageSize = PageSize.Fill
) {
ItemGreeting(greetings[it])
}

Each page in the pager is represented by the ItemGreeting composable, which encapsulates the content and layout for individual greetings.

@Composable
fun ItemGreeting(item:Triple<Int,String,String>){

Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxHeight()
.padding(start = 24.dp, end = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(painter = painterResource(id = item.first),
contentDescription = "image",
contentScale = ContentScale.Crop,
modifier = Modifier
.height(300.dp)
.width(240.dp)
.clip(RoundedCornerShape(10.dp))
)

Spacer(modifier = Modifier.height(64.dp))

Text(
text = item.second,
fontFamily = FontFamily(Font(R.font.sen_extrabold)),
fontSize = 24.sp,
color = colorResource(id = R.color.blue_dark)

)

Spacer(modifier = Modifier.height(24.dp))

Text(
text = item.third,
fontFamily = FontFamily(Font(R.font.sen_regular)),
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = colorResource(id = R.color.blue_dark3)
)


}

}

Adding Interactivity with Buttons and Indicators

Our feature introduction screen wouldn’t be complete without user interaction elements. We’ve included an OrangeButton for navigation and a DotsIndicator to visually represent the number of pages.

@Composable
fun OrangeButton(
title:String,
modifier: Modifier = Modifier,
onClick:()->Unit
){

Button(
modifier = modifier
.fillMaxWidth()
.height(56.dp),
shape = RoundedCornerShape(12),
colors = ButtonDefaults.textButtonColors(colorResource(id = R.color.orange)),
onClick = {
onClick()
}) {
Text(
text = title,
fontFamily = FontFamily(Font(R.font.sen_regular)),
fontSize = 16.sp,
color = colorResource(id = R.color.white)
)
}
}

@Composable
fun DotsIndicator(
totalDots : Int,
selectedIndex : Int,
selectedColor: Int,
unSelectedColor: Int,
){

LazyRow(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight()

) {

items(totalDots) { index ->
if (index == selectedIndex) {
Box(
modifier = Modifier
.size(10.dp)
.clip(CircleShape)
.background(colorResource(id = selectedColor))
)
} else {
Box(
modifier = Modifier
.size(10.dp)
.clip(CircleShape)
.background(colorResource(id = unSelectedColor))
)
}

if (index != totalDots - 1) {
Spacer(modifier = Modifier.padding(horizontal = 8.dp))
}
}
}
}

Conclusion

In this exploration of Jetpack Compose, we’ve crafted a feature introduction screen using HorizontalPager to guide users through the app's key elements. Leveraging Compose's declarative syntax and powerful composables, we've created an engaging and visually appealing user experience.

Happy Composing!

--

--

Murad
Murad

Written by Murad

Android Developer | Jetpack Compose | KMP

No responses yet