Reputation: 66674
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 want only one of the icons in Toolbar menu to be visible while 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"/>
Upvotes: 38
Views: 21570
Reputation: 102
I've handled all this and made the Action bar seamlessly. Custom ActionBar with Icon Visibility Handling in Jetpack Compose
This is how you can use the TopAppBar in All screen.
var menuExpanded by remember {
mutableStateOf(false)
}
TopAppBar(title = {
TopAppBarTitle(title)
},
colors = TopAppBarDefaults.topAppBarColors(containerColor = ZNMaterialTheme.colors.background),
navigationIcon = {
IconButton(onClick = {
onBackPressed.invoke()
}) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Back",
tint = Color.Black
)
}
},
actions = {
ActionsMenu(
items = listOf(
ActionMenuItem.IconMenuItem.AlwaysShown(
title = "Search",
contentDescription = "Search",
onClick = {},
icon = Icons.Filled.Search,
),
ActionMenuItem.IconMenuItem.AlwaysShown(
title = "Favorite",
contentDescription = "Favorite",
onClick = {},
icon = Icons.Filled.FavoriteBorder,
),
ActionMenuItem.IconMenuItem.ShownIfRoom(
title = "Refresh",
contentDescription = "Refresh",
onClick = {},
icon = Icons.Filled.Refresh
),
ActionMenuItem.NeverShown(
title = "Settings",
onClick = {},
),
ActionMenuItem.NeverShown(
title = "About",
onClick = {},
)
),
isOpen = menuExpanded,
onToggleOverflow = { menuExpanded = !menuExpanded },
maxVisibleItems = 3,
)
}
)
here is my github gist : https://gist.github.com/idineshgovind/0f3ccc925354b0c30cd22f640f81b41e
Upvotes: 0
Reputation: 28793
Thanks to Oya Canli and Phil Dukhov I added an overflow menu positioned to the right with the ability to collapse after click.
Using Box
you will have a dropdown menu in a right upper angle, not left.
@Composable
private fun ToolbarMenu() {
var expanded by remember { mutableStateOf(false) }
Box {
IconButton(onClick = {
expanded = !expanded
}) {
Icon(
imageVector = Icons.Outlined.MoreVert,
// painter = painterResource(id = R.drawable.more),
contentDescription = null,
)
}
DropdownMenu(
modifier = Modifier.background(color = Color.White),
expanded = expanded,
onDismissRequest = { expanded = false }
) {
DropDownItem1(onClick = { expanded = false })
HorizontalDivider()
DropDownItem2(onClick = { expanded = false })
}
}
}
@Composable
fun DropDownItem1(onClick: () -> Unit) {
DropdownMenuItem(
trailingIcon = {
Icon(
painter = painterResource(id = R.drawable.share),
contentDescription = stringResource(id = R.string.share),
)
},
text = {
Text(
text = stringResource(R.string.share),
style = ...,
)
},
onClick = onClick,
)
}
Usage:
TopAppBar(
...
actions = { ToolbarMenu() }
),
Upvotes: 0
Reputation: 2516
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))
}
}
Upvotes: 29
Reputation: 6952
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
Upvotes: 61
Reputation: 2710
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
Full pastebin is here: https://gist.github.com/MachFour/369ebb56a66e2f583ebfb988dda2decf
Upvotes: 14