How to control DropDownMenu position in Jetpack Compose
Asked Answered
R

4

32

I have a row with a text align at the start and a image align at the end. When I press the image I'm showing a DropdownMenu, but this appear in the start of the row and I want that appear at the end of the row.

I'm trying to use Alignment.centerEnd in Modifier component but is not working.

How can I do that the popup appear at the end of the row?

@Composable
fun DropdownDemo(currentItem: CartListItems) {
    var expanded by remember { mutableStateOf(false) }
    Box(modifier = Modifier
        .fillMaxWidth()) {
        Text(modifier = Modifier.align(Alignment.TopStart),
            text = currentItem.type,
            color = colorResource(id = R.color.app_grey_dark),
            fontSize = 12.sp)
        Image(painter = painterResource(R.drawable.three_dots),
            contentDescription = "more options button",
            Modifier
                .padding(top = 5.dp, bottom = 5.dp, start = 5.dp)
                .align(Alignment.CenterEnd)
                .width(24.dp)
                .height(6.75.dp)
                .clickable(indication = null,
                    interactionSource = remember { MutableInteractionSource() },
                    onClick = {
                        expanded = true
                    }))
        DropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false },
            modifier = Modifier
                .background(
                    Color.LightGray
                ).align(Alignment.CenterEnd),
        ) {
            DropdownMenuItem(onClick = { expanded = false }) {
                Text("Delete")
            }
            DropdownMenuItem(onClick = { expanded = false }) {
                Text("Save")
            }
        }
    }
}
Rattly answered 10/8, 2021 at 14:8 Comment(0)
G
64

As documentation says:

A DropdownMenu behaves similarly to a Popup, and will use the position of the parent layout to position itself on screen.

You need to put DropdownMenu together with the caller view in a Box. In this case DropdownMenu will appear under the caller view.

var expanded by remember { mutableStateOf(false) }

Box {
    // your button to expand the menu
    Image(
        painter = painterResource(R.drawable.test),
        contentDescription = "more options button",
        modifier = Modifier
            .clickable {
                expanded = true
            }
    )
    DropdownMenu(
        expanded = expanded,
        onDismissRequest = { expanded = false },
    ) {
        DropdownMenuItem(onClick = { expanded = false }) {
            Text("Delete")
        }
        DropdownMenuItem(onClick = { expanded = false }) {
            Text("Save")
        }
    }
}
Genevivegenevra answered 10/8, 2021 at 14:18 Comment(3)
This code snippet will always show the dropdown menu in the left side. Is there any way to get it to show in the center?Hazard
@Hazard it actually automatically detects the direction, if there is no space available to display on the right side, it will show it on the left side. I'm not sure if the material guidelines thinks displaying centered is a good idea, if you find such information - you can open a feature request. So far, the only thing you can adjust is the offset - you need to measure the width of the element as shown here and add half that value to the offset parameter.Genevivegenevra
Note that the menu will always be placed in the top-left corner of the screen for the Live Preview inside Android Studio. However the placement is correct on the emulator or a physical device.Ferrara
A
3

When I press the image I'm showing a DropdownMenu, but this appear in the start of the row and I want that appear at the end of the row.

This is simple to achieve. Use contentAlignment = Alignment.TopStart on the parent Box of the DropdownMenu.

But it's also possible to place the DropDownMenu on the touch position by intercepting the touch events using Modifier.pointerInteropFilter().

Column(Modifier.padding(12.dp)) {
  var offset = Offset.Zero
  var dropDownExpanded by remember { mutableStateOf(false) }

  Card(
      Modifier.fillMaxWidth()
          .shadow(1.dp, CardDefaults.shape)
          .pointerInteropFilter {
            offset = Offset(it.x, it.y)
            false
          }
          .combinedClickable(onClick = {}, onLongClick = { dropDownExpanded = true })) {
        Box(contentAlignment = Alignment.TopStart) {
          Text(
              "Long press me",
              Modifier.fillMaxWidth().padding(vertical = 50.dp),
              textAlign = TextAlign.Center)
          Box {
            DropdownMenu(
                expanded = dropDownExpanded,
                offset = DpOffset(pxToDp(offset.x), pxToDp(offset.y)),
                onDismissRequest = { dropDownExpanded = false }) {
                  DropdownMenuItem(
                      text = { Text("Dismiss me") }, onClick = { dropDownExpanded = false })
                }
          }
        }
      }
}

Result:

Accommodate answered 15/8, 2023 at 11:33 Comment(0)
H
0

You May use Surface and Box with fillmaxWidth of fillmaxsize:

Surface(Modifier.fillMaxWidth()) {
                            Box(Modifier.fillMaxWidth()) {

                                Row(Modifier.align(Alignment.TopEnd)) {
                                    DropdownMenu(
                                        expanded = expanded_session,
                                        onDismissRequest = { expanded_session = false },
                                        modifier = Modifier.fillMaxWidth(0.5f).fillMaxHeight(0.6f)
                                    ) {
                                        sessions.forEach { item ->
                                            DropdownMenuItem(onClick = {
                                                selected_Session = item
                                                expanded_session = false
                                            },
                                                text = { Text(text = item) }
                                            )
                                        }
                                    }
                                }
                            }
                        }
Halfblooded answered 17/5 at 5:27 Comment(0)
I
-1

Use the offset parameter of the DropdownMenu().

DropdownMenu(
    offset = DpOffset(x = (-66).dp, y = (-10).dp)
)

Change the x and y values. They accept both positive and negative values.

Intorsion answered 12/7, 2022 at 14:26 Comment(5)
This would behave too oddLayne
Can you explain above the code and values.Melvamelvena
But why -66 and -10?Anyplace
I upvoted because it working for meUnwritten
This link is redirecting to somewhere else, don't clickResiduary

© 2022 - 2024 — McMap. All rights reserved.