(de)composing action bars continued
Tags: android, jetpackcompose
Published: 2021-02-21 13:57:14

Welcome to the second installment of *(de)composing action bars*. In the first part we took a look at how to set titles and subtitles. And we briefly revisited back navigation. Todays' post is based upon a small example called *ScaffoldDemo*. The corresponding GitHub repo is linked at the bottom of this article.

Back in the very old days Android devices featured hardware keys that navigated back or brought you to the home screen. There was also a dedicated menu buttn that opened the so-called options menu. You can spot it, for example, in the Wikipedia article [HTC Dream](https://en.wikipedia.org/wiki/HTC_Dream). Later, those hardware keys became virtual. And the options menu moved to action bar, or appbar. To our (the developers) relief, the [underlying machanics](https://developer.android.com/guide/topics/ui/menus#options-menu) remained the same. These included:

- creating an xml file that defines the menu
- implement `onCreateOptionsMenu()` (usually your code will inflate the xml file
- react to item clicks in `onOptionsItemSelected()`

Now, how does this translate to the declarative world? Actually, quite straight forward:

```kotlin
@Composable
fun Content() {
  ScaffoldDemoTheme {
    Scaffold(topBar = {
      TopAppBar(title = {
        Column {
          Text("Title")
          Text("Subtitle", style = MaterialTheme.typography.subtitle1)
        }
      },
        actions = {
          IconButton(onClick = {
            println("Icons.Default.Add")
          }) {
            Icon(Default.Add, null)
          }
          IconButton(onClick = {
            println("Icons.Default.Delete")
          }) {
            Icon(Default.Delete, null)
          }
        })
    }) {
      MyContent()
    }
  }
}
```

![Two Compose actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2fa8kv6l31lk758gic5.png)

The docs say:

> The actions displayed at the end of the `TopAppBar`. 
> This should typically be `IconButton`s. The default 
> layout here is a `Row`, so icons inside will be placed
> horizontally.

While this is convenient and easy to implement it offers less flexibility than the *old* approach. The following file represents a part of the options menu of one of my apps:

```xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">
  <item
    android:id="@+id/new_entry"
    android:icon="@drawable/ic_baseline_create_24"
    app:showAsAction="ifRoom"
    android:title="@string/menu_new_entry" />
  <item
    android:id="@+id/set_date"
    android:title="@string/menu_set_date" />
  …
</menu>
```

Did you notice `ifRoom`? That means: put it on the appbar as a visible icon if there is, well, enough room, otherwise put it in a menu, which, by the way, looks like this:

![Opened options menu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d81wx0mf2qgsv68i3qv.png)

To do something similar in Compose we will use the [DropdownMenu](https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary#dropdownmenu).

> A `DropdownMenu` behaves similarly to a `Popup`, and will use
> the position of the parent layout to position itself on screen.
> Commonly a `DropdownMenu` will be placed in a `Box` with a
> sibling that will be used as the 'anchor'. Note that a
> `DropdownMenu` by itself will not take up any space in a layout,
> as the menu is displayed in a separate window, on top of other
> content.

How does this translate to code?

```kotlin
@Composable
fun Content() {
  var menuOpened by remember { mutableStateOf(false) }
  ScaffoldDemoTheme {
    Scaffold(topBar = {
      TopAppBar(title = {
        Column {
          Text("Title")
          Text("Subtitle", style = MaterialTheme.typography.subtitle1)
        }
      },
        actions = {
          Box {
            IconButton(onClick = {
              menuOpened = true
            }) {
              Icon(Default.MoreVert, null)
              DropdownMenu(expanded = menuOpened,
                onDismissRequest = {
                  menuOpened = false
                }) {
                DropdownMenuItem(onClick = {
                  menuOpened = false
                }) {
                  Text("Item #1")
                }
                Divider()
                DropdownMenuItem(onClick = {
                  menuOpened = false
                }) {
                  Text("Item #2")
                }
              }
            }
          }
        })
    }) {
      MyContent()
    }
  }
}
```

![A DropdownMenu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/igo57q8tmtf2zdkfrj2p.png)

Now, while this looks nice it is not the same layout as in the old world. We could tweak the vertical position like this:

```kotlin
offset = DpOffset(0.dp, (-20).dp),
```

but this leaves at middle of the road:

![Menu appearing roughly in the middle of the three dots](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kekahjra99c3oxfghojg.png)

There is a modifier called `onGloballyPositioned` which we might utilize like this:

```kotlin
var height: Int
Box(modifier = Modifier.onGloballyPositioned {
  height = it.size.height
}) {
  IconButton(onClick = {
```

But we still need to convert this `Int` value to `dp`, because `offset` is a `DpOffset`.

```kotlin
offset = DpOffset(
  0.dp,
  -(LocalDensity.current.density * height).dp
),
```

![A nicely aligned menu](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8kvsx5mtar3as1ywyri.png)

Now, this looks good. What do you think about menu support in Compose? Please share your thoughts in the comments.

---

[source](https://github.com/tkuenneth/android-demos/tree/master/compose/ScaffoldDemo)