Merge "Adds a ScrollState param to dropdown menus" into androidx-main
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -22,7 +22,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
diff --git a/compose/material3/material3/api/public_plus_experimental_current.txt b/compose/material3/material3/api/public_plus_experimental_current.txt
index dedbef4..b2471cb 100644
--- a/compose/material3/material3/api/public_plus_experimental_current.txt
+++ b/compose/material3/material3/api/public_plus_experimental_current.txt
@@ -23,7 +23,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
@@ -511,7 +512,7 @@
}
@androidx.compose.material3.ExperimentalMaterial3Api public interface ExposedDropdownMenuBoxScope {
- method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public default void ExposedDropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.foundation.ScrollState scrollState, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
method public androidx.compose.ui.Modifier exposedDropdownSize(androidx.compose.ui.Modifier, optional boolean matchTextFieldWidth);
method public androidx.compose.ui.Modifier menuAnchor(androidx.compose.ui.Modifier);
}
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index d2b53fb..1f5b379 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -22,7 +22,8 @@
}
public final class AndroidMenu_androidKt {
- method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.foundation.ScrollState scrollState, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,kotlin.Unit> content);
+ method @Deprecated @androidx.compose.runtime.Composable public static void DropdownMenu(boolean expanded, kotlin.jvm.functions.Function0<? extends kotlin.Unit> onDismissRequest, optional androidx.compose.ui.Modifier modifier, optional long offset, optional androidx.compose.ui.window.PopupProperties properties, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.ColumnScope,? extends kotlin.Unit> content);
method @androidx.compose.runtime.Composable public static void DropdownMenuItem(kotlin.jvm.functions.Function0<kotlin.Unit> text, kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional kotlin.jvm.functions.Function0<kotlin.Unit>? leadingIcon, optional kotlin.jvm.functions.Function0<kotlin.Unit>? trailingIcon, optional boolean enabled, optional androidx.compose.material3.MenuItemColors colors, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource interactionSource);
}
diff --git a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
index e5369cc..1d6480b 100644
--- a/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
+++ b/compose/material3/material3/integration-tests/material3-catalog/src/main/java/androidx/compose/material3/catalog/library/model/Examples.kt
@@ -81,6 +81,7 @@
import androidx.compose.material3.samples.LeadingIconTabs
import androidx.compose.material3.samples.LinearProgressIndicatorSample
import androidx.compose.material3.samples.MenuSample
+import androidx.compose.material3.samples.MenuWithScrollStateSample
import androidx.compose.material3.samples.ModalBottomSheetSample
import androidx.compose.material3.samples.ModalNavigationDrawerSample
import androidx.compose.material3.samples.NavigationBarItemWithBadge
@@ -618,6 +619,13 @@
MenuSample()
},
Example(
+ name = ::MenuWithScrollStateSample.name,
+ description = MenusExampleDescription,
+ sourceUrl = MenusExampleSourceUrl
+ ) {
+ MenuWithScrollStateSample()
+ },
+ Example(
name = ::ExposedDropdownMenuSample.name,
description = MenusExampleDescription,
sourceUrl = MenusExampleSourceUrl
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
index 0c5f3c3..b6e3445 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/MenuSamples.kt
@@ -20,6 +20,7 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.outlined.Edit
@@ -32,6 +33,7 @@
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -47,7 +49,11 @@
fun MenuSample() {
var expanded by remember { mutableStateOf(false) }
- Box(modifier = Modifier.fillMaxSize().wrapContentSize(Alignment.TopStart)) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)
+ ) {
IconButton(onClick = { expanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
}
@@ -87,3 +93,43 @@
}
}
}
+
+@Preview
+@Sampled
+@Composable
+fun MenuWithScrollStateSample() {
+ var expanded by remember { mutableStateOf(false) }
+ val scrollState = rememberScrollState()
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .wrapContentSize(Alignment.TopStart)
+ ) {
+ IconButton(onClick = { expanded = true }) {
+ Icon(Icons.Default.MoreVert, contentDescription = "Localized description")
+ }
+ DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = { expanded = false },
+ scrollState = scrollState
+ ) {
+ repeat(30) {
+ DropdownMenuItem(
+ text = { Text("Item ${it + 1}") },
+ onClick = { /* TODO */ },
+ leadingIcon = {
+ Icon(
+ Icons.Outlined.Edit,
+ contentDescription = null
+ )
+ })
+ }
+ }
+ LaunchedEffect(expanded) {
+ if (expanded) {
+ // Scroll to show the bottom menu items.
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+}
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
index 66ae652..24bde2e 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/ExposedDropdownMenuTest.kt
@@ -22,7 +22,9 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -35,9 +37,11 @@
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsFocused
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.assertTextContains
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
@@ -198,11 +202,17 @@
modifier = Modifier.padding(8.dp),
) {
TextField(
- modifier = Modifier.menuAnchor().then(
- if (index == testIndex) Modifier.testTag(TFTag).onSizeChanged {
- textFieldSize = it
- } else { Modifier }
- ),
+ modifier = Modifier
+ .menuAnchor()
+ .then(
+ if (index == testIndex) Modifier
+ .testTag(TFTag)
+ .onSizeChanged {
+ textFieldSize = it
+ } else {
+ Modifier
+ }
+ ),
value = selectedOptionText,
onValueChange = { selectedOptionText = it },
label = { Text("Label") },
@@ -315,7 +325,10 @@
setContent {
Box {
ExposedDropdownMenuBox(expanded = true, onExpandedChange = {}) {
- Box(Modifier.menuAnchor().size(20.dp))
+ Box(
+ Modifier
+ .menuAnchor()
+ .size(20.dp))
}
}
}
@@ -334,6 +347,49 @@
// Should not have crashed.
}
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Test
+ fun withScrolledContent() {
+ rule.setMaterialContent(lightColorScheme()) {
+ Box(Modifier.fillMaxSize()) {
+ ExposedDropdownMenuBox(
+ modifier = Modifier.align(Alignment.Center),
+ expanded = true,
+ onExpandedChange = { }
+ ) {
+ val scrollState = rememberScrollState()
+ TextField(
+ modifier = Modifier.menuAnchor(),
+ value = "",
+ onValueChange = { },
+ label = { Text("Label") },
+ )
+ ExposedDropdownMenu(
+ expanded = true,
+ onDismissRequest = { },
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(with(LocalDensity.current) { 70.toDp() })
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
@Composable
fun ExposedDropdownMenuForTest(
expanded: Boolean,
@@ -349,7 +405,9 @@
onExpandedChange = { onExpandChange(!expanded) }
) {
TextField(
- modifier = Modifier.menuAnchor().testTag(TFTag)
+ modifier = Modifier
+ .menuAnchor()
+ .testTag(TFTag)
.onGloballyPositioned {
onTextFieldBoundsChanged?.invoke(it.boundsInRoot())
},
@@ -368,9 +426,11 @@
colors = ExposedDropdownMenuDefaults.textFieldColors()
)
ExposedDropdownMenu(
- modifier = Modifier.testTag(EDMTag).onGloballyPositioned {
- onMenuBoundsChanged?.invoke(it.boundsInRoot())
- },
+ modifier = Modifier
+ .testTag(EDMTag)
+ .onGloballyPositioned {
+ onMenuBoundsChanged?.invoke(it.boundsInRoot())
+ },
expanded = expanded,
onDismissRequest = { onExpandChange(false) }
) {
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
index 2810336..74c0d5c 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuScreenshotTest.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Email
@@ -100,10 +101,14 @@
@Composable
private fun TestMenu(enabledItems: Boolean) {
- Box(Modifier.testTag(testTag).padding(20.dp), contentAlignment = Alignment.Center) {
+ Box(
+ Modifier
+ .testTag(testTag)
+ .padding(20.dp), contentAlignment = Alignment.Center) {
DropdownMenuContent(
expandedStates = MutableTransitionState(initialState = true),
- transformOriginState = remember { mutableStateOf(TransformOrigin.Center) }
+ transformOriginState = remember { mutableStateOf(TransformOrigin.Center) },
+ scrollState = rememberScrollState()
) {
DropdownMenuItem(
text = { Text("Edit") },
diff --git a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
index 124eac9..0df58c9 100644
--- a/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
+++ b/compose/material3/material3/src/androidAndroidTest/kotlin/androidx/compose/material3/MenuTest.kt
@@ -21,6 +21,8 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -28,7 +30,8 @@
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.testTag
-import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsNotDisplayed
import androidx.compose.ui.test.hasAnyDescendant
import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.isPopup
@@ -51,7 +54,6 @@
@MediumTest
@RunWith(AndroidJUnit4::class)
-@OptIn(ExperimentalTestApi::class)
class MenuTest {
@get:Rule
val rule = createComposeRule()
@@ -61,7 +63,11 @@
var expanded by mutableStateOf(false)
rule.setContent {
- Box(Modifier.requiredSize(20.dp).background(color = Color.Blue)) {
+ Box(
+ Modifier
+ .requiredSize(20.dp)
+ .background(color = Color.Blue)
+ ) {
DropdownMenu(
expanded = expanded,
onDismissRequest = {}
@@ -103,13 +109,25 @@
fun menu_hasExpectedSize() {
rule.setContent {
with(LocalDensity.current) {
- Box(Modifier.requiredSize(20.toDp()).background(color = Color.Blue)) {
+ Box(
+ Modifier
+ .requiredSize(20.toDp())
+ .background(color = Color.Blue)
+ ) {
DropdownMenu(
expanded = true,
onDismissRequest = {}
) {
- Box(Modifier.testTag("MenuContent1").size(70.toDp()))
- Box(Modifier.testTag("MenuContent2").size(130.toDp()))
+ Box(
+ Modifier
+ .testTag("MenuContent1")
+ .size(70.toDp())
+ )
+ Box(
+ Modifier
+ .testTag("MenuContent2")
+ .size(130.toDp())
+ )
}
}
}
@@ -128,6 +146,43 @@
}
}
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Test
+ fun menu_scrolledContent() {
+ rule.setContent {
+ with(LocalDensity.current) {
+ Box(
+ Modifier
+ .requiredSize(20.toDp())
+ .background(color = Color.Blue)
+ ) {
+ val scrollState = rememberScrollState()
+ DropdownMenu(
+ expanded = true,
+ onDismissRequest = {},
+ scrollState = scrollState
+ ) {
+ repeat(100) {
+ Box(
+ Modifier
+ .testTag("MenuContent ${it + 1}")
+ .size(70.toDp())
+ )
+ }
+ }
+ LaunchedEffect(Unit) {
+ scrollState.scrollTo(scrollState.maxValue)
+ }
+ }
+ }
+ }
+
+ rule.waitForIdle()
+
+ rule.onNodeWithTag("MenuContent 1").assertIsNotDisplayed()
+ rule.onNodeWithTag("MenuContent 100").assertIsDisplayed()
+ }
+
@Test
fun menu_positioning_bottomEnd() {
val screenWidth = 500
@@ -349,7 +404,9 @@
DropdownMenuItem(
text = { Box(Modifier.requiredSize(40.dp)) },
onClick,
- modifier = Modifier.testTag("MenuItem").clickable(onClick = onClick),
+ modifier = Modifier
+ .testTag("MenuItem")
+ .clickable(onClick = onClick),
)
}
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
index 3a96fdc..0acd46a 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/AndroidMenu.android.kt
@@ -17,10 +17,12 @@
package androidx.compose.material3
import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -67,7 +69,83 @@
* @param expanded whether the menu is expanded or not
* @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
* outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
* @param offset [DpOffset] to be added to the position of the menu
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
+ */
+@OptIn(ExperimentalMaterial3Api::class)
+@Suppress("ModifierParameter")
+@Deprecated(
+ level = DeprecationLevel.HIDDEN,
+ replaceWith = ReplaceWith(
+ expression = "DropdownMenu(expanded,onDismissRequest, modifier, offset, " +
+ "rememberScrollState(), properties, content)",
+ "androidx.compose.foundation.rememberScrollState"
+ ),
+ message = "Replaced by a DropdownMenu function with a ScrollState parameter"
+)
+@Composable
+fun DropdownMenu(
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ modifier: Modifier = Modifier,
+ offset: DpOffset = DpOffset(0.dp, 0.dp),
+ properties: PopupProperties = PopupProperties(focusable = true),
+ content: @Composable ColumnScope.() -> Unit
+) = DropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ modifier = modifier,
+ offset = offset,
+ scrollState = rememberScrollState(),
+ properties = properties,
+ content = content
+)
+
+/**
+ * <a href="https://m3.material.io/components/menus/overview" class="external" target="_blank">Material Design dropdown menu</a>.
+ *
+ * Menus display a list of choices on a temporary surface. They appear when users interact with a
+ * button, action, or other control.
+ *
+ * ![Dropdown menu image](https://developer.android.com/images/reference/androidx/compose/material3/menu.png)
+ *
+ * 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.
+ *
+ * The [content] of a [DropdownMenu] will typically be [DropdownMenuItem]s, as well as custom
+ * content. Using [DropdownMenuItem]s will result in a menu that matches the Material
+ * specification for menus. Also note that the [content] is placed inside a scrollable [Column],
+ * so using a [LazyColumn] as the root layout inside [content] is unsupported.
+ *
+ * [onDismissRequest] will be called when the menu should close - for example when there is a
+ * tap outside the menu, or when the back key is pressed.
+ *
+ * [DropdownMenu] changes its positioning depending on the available space, always trying to be
+ * fully visible. It will try to expand horizontally, depending on layout direction, to the end of
+ * its parent, then to the start of its parent, and then screen end-aligned. Vertically, it will
+ * try to expand to the bottom of its parent, then from the top of its parent, and then screen
+ * top-aligned. An [offset] can be provided to adjust the positioning of the menu for cases when
+ * the layout bounds of its parent do not coincide with its visual bounds. Note the offset will
+ * be applied in the direction in which the menu will decide to expand.
+ *
+ * Example usage:
+ * @sample androidx.compose.material3.samples.MenuSample
+ *
+ * Example usage with a [ScrollState] to control the menu items scroll position:
+ * @sample androidx.compose.material3.samples.MenuWithScrollStateSample
+ *
+ * @param expanded whether the menu is expanded or not
+ * @param onDismissRequest called when the user requests to dismiss the menu, such as by tapping
+ * outside the menu's bounds
+ * @param modifier [Modifier] to be applied to the menu's content
+ * @param offset [DpOffset] to be added to the position of the menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
+ * @param properties [PopupProperties] for further customization of this popup's behavior
+ * @param content the content of this dropdown menu, typically a [DropdownMenuItem]
*/
@Suppress("ModifierParameter")
@Composable
@@ -76,6 +154,7 @@
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
+ scrollState: ScrollState = rememberScrollState(),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
) {
@@ -100,6 +179,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier,
content = content
)
diff --git a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
index 9fe9b8c..b863609 100644
--- a/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
+++ b/compose/material3/material3/src/androidMain/kotlin/androidx/compose/material3/ExposedDropdownMenu.kt
@@ -20,6 +20,7 @@
import android.view.View
import android.view.ViewTreeObserver
import androidx.compose.animation.core.MutableTransitionState
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.waitForUpOrCancellation
@@ -28,6 +29,7 @@
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.material.icons.Icons
@@ -239,6 +241,7 @@
* @param onDismissRequest called when the user requests to dismiss the menu, such as by
* tapping outside the menu's bounds
* @param modifier the [Modifier] to be applied to this menu
+ * @param scrollState a [ScrollState] to used by the menu's content for items vertical scrolling
* @param content the content of the menu
*/
@Composable
@@ -246,6 +249,7 @@
expanded: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
+ scrollState: ScrollState = rememberScrollState(),
content: @Composable ColumnScope.() -> Unit
) {
// TODO(b/202810604): use DropdownMenu when PopupProperties constructor is stable
@@ -277,6 +281,7 @@
DropdownMenuContent(
expandedStates = expandedStates,
transformOriginState = transformOriginState,
+ scrollState = scrollState,
modifier = modifier.exposedDropdownSize(),
content = content
)
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
index 0ff7c120..318985d 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/Menu.kt
@@ -21,6 +21,7 @@
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
+import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
@@ -34,7 +35,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.tokens.MenuTokens
@@ -66,6 +66,7 @@
internal fun DropdownMenuContent(
expandedStates: MutableTransitionState<Boolean>,
transformOriginState: MutableState<TransformOrigin>,
+ scrollState: ScrollState,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
@@ -133,7 +134,7 @@
modifier = modifier
.padding(vertical = DropdownMenuVerticalPadding)
.width(IntrinsicSize.Max)
- .verticalScroll(rememberScrollState()),
+ .verticalScroll(scrollState),
content = content
)
}