Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Toolbar with overflow menu be created with Jetpack Compose?

How can menu icons of Toolbar can be turned into overflow in Compose?

Scaffold(
    topBar = {
        TopAppBar(
            title = {
                Text(text = "LayoutsCodelab")
            },
            actions = {
                IconButton(onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Favorite)
                }

                IconButton(onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Refresh)
                }

                IconButton(
                    onClick = { /* doSomething() */ }) {
                    Icon(Icons.Filled.Call)
                }

            }
        )
    },
    bottomBar = {
        BottomNavigationLayout()
    }
) { innerPadding ->
    PhotoCard(Modifier.padding(innerPadding))
}

I only want one of the icons in Toolbar menu to be visible others to be added to overflow menu like done with xml using app:showAsAction="never"

<item
    android:id="@+id/action_sign_out"
    android:title="@string/toolbar_sign_out"
    app:showAsAction="never"/>
like image 392
Thracian Avatar asked Dec 19 '20 19:12

Thracian


People also ask

What is the action overflow menu used for?

The action overflow in the action bar provides access to your app's less frequently used actions. The overflow icon only appears on phones that have no menu hardware keys. Phones with menu keys display the action overflow when the user presses the key. Action overflow is pinned to the right side.

What is overflow menu icon?

4 Comments. The overflow icon is a common UI convention that's leveraged throughout Android to hide settings and other unimportant options. Google is now replacing it in the Play Store with a “tap & hold” gesture and bottom sheet menu.


3 Answers

You have to provide the OverFlowMenu yourself, e.g.:

@Preview
@Composable
fun PreviewOverflowMenu() {
    OverflowMenuTest()
}

@Composable
fun OverflowMenuTest() {
    var showMenu by remember { mutableStateOf(false) }

    TopAppBar(
        title = { Text("Title") },
        actions = {
            IconButton(onClick = { /*TODO*/ }) {
                Icon(Icons.Default.Favorite)
            }
            IconButton(onClick = { showMenu = !showMenu }) {
                Icon(Icons.Default.MoreVert)
            }
            DropdownMenu(
                expanded = showMenu,
                onDismissRequest = { showMenu = false }
            ) {
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Refresh)
                }
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Call)
                }
            }
        }
    )
}

Edit: Updated for Compose 1.0.0-beta08

like image 165
jns Avatar answered Nov 15 '22 08:11

jns


Inspired by @jns's answer, I made an ActionMenu composable which takes a list of ActionItemSpec objects. and displays them with an overflow menu if necessary. I modelled the ActionItemSpec a bit like the old XML menu item entries, but added an onClick lambda.

It's used like this

@Preview
@Composable
fun PreviewActionMenu() {
    val items = listOf(
        ActionItemSpec("Call", Icons.Default.Call, ActionItemMode.ALWAYS_SHOW) {},
        ActionItemSpec("Send", Icons.Default.Send, ActionItemMode.IF_ROOM) {},
        ActionItemSpec("Email", Icons.Default.Email, ActionItemMode.IF_ROOM) {},
        ActionItemSpec("Delete", Icons.Default.Delete, ActionItemMode.IF_ROOM) {},
    )
    TopAppBar(
        title = { Text("App bar") },
        navigationIcon = {
            IconButton(onClick = {}) {
                Icon(Icons.Default.Menu, "Menu")
            }
        },
        actions = {
            // show 3 icons including overflow
            ActionMenu(items, defaultIconSpace = 3)
        }
    )
}

and the preview looks like this

Navigation hamburger, "App bar" title, phone icon, send icon, three vertical dots overflow menu

Full pastebin is here: https://gist.github.com/MachFour/369ebb56a66e2f583ebfb988dda2decf

like image 29
machfour Avatar answered Nov 15 '22 09:11

machfour


I modified a bit @jns's answer to make it more modular and reusable. This is the reusable OverflowMenu:

@Composable
fun OverflowMenu(content: @Composable () -> Unit) {
    var showMenu by remember { mutableStateOf(false) }

    IconButton(onClick = {
        showMenu = !showMenu
    }) {
        Icon(
            imageVector = Icons.Outlined.MoreVert,
            contentDescription = stringResource(R.string.more),
        )
    }
    DropdownMenu(
        expanded = showMenu,
        onDismissRequest = { showMenu = false }
    ) {
        content()
    }
}

And this is how it is used inside the TopAppBar:

TopAppBar(
        title = {
            Text(text = stringResource(R.string.my_title))
        },
        actions = {
            OverflowMenu {
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Text("Settings")
                }
                DropdownMenuItem(onClick = { /*TODO*/ }) {
                    Text("Bookmarks")
                }
            }
        }
    )

We can possibly add icons to DropDownMenuItems if desired. And these items can be extracted as reusable composables as well. If there are other action buttons that you want to show as iconified buttons on the menu(i.e. show as action), you should put them before the OverflowMenu.

TopAppBar(
        title = {
            Text(text = stringResource(R.string.bookmark))
        },
        actions = {
            //This icon will be shown on the top bar, on the left of the overflow menu
            IconButton(onClick = { /*TODO*/ }) {
                Icon(Icons.Filled.FavoriteBorder, stringResource(R.string.cd_favorite_item))
            }
            OverflowMenu {
                SettingsDropDownItem(onClick = { /*TODO*/ })
                BookmarksDropDownItem(onClick = { /*TODO*/ })
            }
        }
    )

.

@Composable
   fun SettingsDropDownItem(onClick : () -> Unit) {
      //Drop down menu item with an icon on its left
      DropdownMenuItem(onClick = onClick) {
         Icon(Icons.Filled.Settings,
            contentDescription = stringResource(R.string.settings),
            modifier = Modifier.size(24.dp))
         Spacer(modifier = Modifier.width(8.dp))
         Text(stringResource(R.string.settings))
     }
  }

  @Composable
  fun BookmarksDropDownItem(onClick : () -> Unit) {
     //Drop down menu item with an icon on its left
     DropdownMenuItem(onClick = onClick) {
        Icon(painter = painterResource(R.drawable.ic_bookmark_filled),
            contentDescription = stringResource(R.string.bookmark),
            modifier = Modifier.size(24.dp))
        Spacer(modifier = Modifier.width(8.dp))
        Text(stringResource(R.string.bookmark))
    }
}
like image 27
Oya Canli Avatar answered Nov 15 '22 07:11

Oya Canli