Blog Infos
Author
Published
Topics
Published

Showing menu items in multiple food & drinks apps is one of the best use cases for multiple lists interaction, Let鈥檚 create a simple implementation with Jetpack Compose 馃殌

We鈥檒l cover all of the following points :

  • Show menu items list labeled by sections
  • Show sections list and highlight the selected one
  • On menu items list scroll, highlight the sections list with the displayed section
  • On click on section name, jump to the menu items list section position

 

 

Show menu items list labeled by sections

This is a simple list for all menu items labeled & indexed by sections title

  • itemsListState聽is a聽LazyListState聽which will be used to handle auto scroll which will be discussed later
  • onPostScroll聽used to detect a scroll action on the menu items list, called from聽nestedScroll聽modifier

 

@Composable
fun MenuItemsView(
menuSections: List<MenuSections>,
itemsListState: LazyListState,
onPostScroll: () -> Unit
) {
LazyColumn(
state = itemsListState,
modifier = Modifier
.padding()
.nestedScroll(object : NestedScrollConnection {
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource
): Offset {
onPostScroll()
return super.onPostScroll(consumed, available, source)
}
})
) {
menuSections.forEach { section ->
item {
Text(
modifier = Modifier.padding(10.dp),
text = section.title,
style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold),
color = Purple200
)
MenuItemView(section)
}
}
}
}

And this is the聽MenuItemView, just a simple view to show section items:

@Composable
fun MenuItemView(section: MenuSections) {
Column {
section.menuItems.forEach { menuItem ->
Column(Modifier.padding(10.dp)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
text = menuItem.title,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold
),
color = Color.DarkGray
)
Text(
text = menuItem.price,
style = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold
),
color = Color.DarkGray
)
}
Image(
modifier = Modifier.size(100.dp),
painter = painterResource(id = menuItem.imageID),
contentScale = ContentScale.FillWidth,
contentDescription = "Menu Item Image"
)
}
Divider(Modifier.padding(top = 20.dp))
}
}
}
}
view raw MenuItemView.kt hosted with ❤ by GitHub
Show sections list and highlight the selected one

This is a simple list to show sections titles in a row and used聽SectionTextView to show and highlight the selected section:

@Composable
fun MenuSectionsView(
selectedIndex: Int,
menuSections: List<MenuSections>,
sectionsListState: LazyListState,
onClick: (sectionIndex: Int) -> Unit
) {
LazyRow(
modifier = Modifier.padding(),
state = sectionsListState
) {
menuSections.forEachIndexed { i, section ->
item {
SectionTextView(
modifier = Modifier
.padding(horizontal = 10.dp)
.clickable { onClick(i) },
text = section.title,
isSelected = selectedIndex == i
)
}
}
}
}

SectionTextView聽will also show an underline to the selected section item, the underline width聽textWidth聽should always match the text size so it is measured on聽onGloballyPositioned聽modifier:

@Composable
fun SectionTextView(
modifier: Modifier = Modifier,
text: String,
isSelected: Boolean
) {
Column(modifier) {
var textWidth by remember { mutableStateOf(0.dp) }
val density = LocalDensity.current
Text(
modifier = Modifier.onGloballyPositioned {
textWidth = with(density) { it.size.width.toDp() } //update text width value according to the content size
},
text = text,
style = TextStyle(fontSize = 24.sp, fontWeight = FontWeight.Bold),
color = if (isSelected) Purple200 else Color.DarkGray
)
//Show the text underline with animation
AnimatedVisibility(
visible = isSelected,
enter = expandHorizontally() + fadeIn(),
exit = shrinkHorizontally() + fadeOut()
) {
Box(
Modifier
.width(textWidth)
.padding(top = 15.dp)
.height(3.dp)
.background(Purple200)
) {}
}
}
}

Job Offers

Job Offers


    Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now

    Senior Android Software Engineer (f/m/d)

    Paradox Cat GmbH
    Munich
    • Full Time
    apply now

    Mobile Engineer

    OLX Group
    Remote, Portugal, Spain, Romania, Poland
    • Full Time
    apply now
Load more listings

OUR VIDEO RECOMMENDATION

,

Branching out to Jetpack Compose

As one of the most widely used social media platforms, Twitter is always hunting for ways to better connect its users. In early 2021 the Client UI team at Twitter began the task of integrating…
Watch Video

Branching out to Jetpack Compose

Nacho L贸pez & Chris Banes
Staff Software Engineer
Twitter

Branching out to Jetpack Compose

Nacho L贸pez & Chr ...
Staff Software Engin ...
Twitter

Branching out to Jetpack Compose

Nacho L贸pez & C ...
Staff Software Engineer
Twitter

Jobs

On menu items list scroll, highlight the sections list with the displayed section

The main parent view which will handle both lists interactions is聽MenuView

It will hold the current selection and handle onPostScroll event from the menu items list view

  • Update the selected section index
  • Scroll through the sections list to the selected section title position
@Composable
fun MenuView(
modifier: Modifier = Modifier,
menuSections: List<MenuSections> = FakeData.menuData
) {
val scope = rememberCoroutineScope()
...
...
MenuItemsView(
menuSections = menuSections,
itemsListState = itemsListState,
onPostScroll = {
val currentSectionIndex = itemsListState.firstVisibleItemIndex
if (selectedSectionIndex != currentSectionIndex) {
selectedSectionIndex = currentSectionIndex
scope.launch {
sectionsListState.animateScrollToItem(currentSectionIndex)
}
}
}
)
}
}
On click on section name, jump to the section menu items list position

MenuViewwill also handle section title click event

  • Update the selected section index
  • Scroll through the items list to the selected section position
  • Scroll through the sections list to the selected section title position
fun MenuView(
modifier: Modifier = Modifier,
menuSections: List<MenuSections> = FakeData.menuData
) {
val scope = rememberCoroutineScope()
...
...
MenuSectionsView(
selectedIndex = selectedSectionIndex,
menuSections = menuSections,
sectionsListState = sectionsListState,
onClick = { sectionIndex ->
selectedSectionIndex = sectionIndex
scope.launch {
sectionsListState.animateScrollToItem(sectionIndex)
itemsListState.animateScrollToItem(sectionIndex)
}
}
)
}
}

Finally the聽MenuViewwill be like this:

fun MenuView(
modifier: Modifier = Modifier,
menuSections: List<MenuSections> = FakeData.menuData
) {
val scope = rememberCoroutineScope()
val sectionsListState = rememberLazyListState()
val itemsListState = rememberLazyListState()
var selectedSectionIndex by remember { mutableStateOf(0) }
Column(modifier) {
Text(
modifier = Modifier.padding(
horizontal = 10.dp,
vertical = 30.dp
),
text = "Menu",
style = TextStyle(fontSize = 28.sp, fontWeight = FontWeight.Bold),
color = Color.DarkGray
)
MenuSectionsView(
selectedIndex = selectedSectionIndex,
menuSections = menuSections,
sectionsListState = sectionsListState,
onClick = { sectionIndex ->
selectedSectionIndex = sectionIndex
scope.launch {
sectionsListState.animateScrollToItem(sectionIndex)
itemsListState.animateScrollToItem(sectionIndex)
}
}
)
Divider()
MenuItemsView(
menuSections = menuSections,
itemsListState = itemsListState,
onPostScroll = {
val currentSectionIndex = itemsListState.firstVisibleItemIndex
if (selectedSectionIndex != currentSectionIndex) {
selectedSectionIndex = currentSectionIndex
scope.launch {
sectionsListState.animateScrollToItem(currentSectionIndex)
}
}
}
)
}
}
view raw MenuView.kt hosted with ❤ by GitHub

That鈥檚 it! You made it! 馃挭

Do you see anything missing? please comment!

Thanks for reading, see you in the next article 馃槉

This article was originally published on聽proandroiddev.com on July 13, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It鈥檚 one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE
blog
Yes! You heard it right. We鈥檒l try to understand the complete OTP (one time…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu