This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 57).
菜单
与 @react-native-menu/menu 兼容的菜单。
一个与 @react-native-menu/menu 的 API 兼容的 MenuView 组件。支持单击(默认)和长按(shouldOpenOnLongPress)两种触发方式。
在内部,此组件封装了平台特定的 @expo/ui 基础组件:
- Android: Jetpack Compose 下拉菜单,锚定到内部的
Pressable触发器。 - iOS: SwiftUI 菜单用于点击触发, SwiftUI 上下文菜单用于长按触发。
如果你需要更底层的控制,请直接使用这些基础组件。
安装
- npx expo install @expo/uiIf you are installing this in an existing React Native app, make sure to install expo in your project.
从 @react-native-menu/menu 迁移
- 将导入从
import { MenuView } from '@react-native-menu/menu'更新为import { MenuView } from '@expo/ui/community/menu'。 - Android 上的
action.image与上游不同。@react-native-menu/menu期望的是一个 drawable 资源名 字符串(例如'ic_menu_add'),它会在android/app/src/main/res/drawable/下解析该资源。这个直接替换版本 不会 解析 drawable 资源名——请改为传入ImageSourcePropType(例如require('@expo/material-symbols/edit.xml'))。在 iOS 上,字符串值会被当作 SF Symbol 名称接受。使用Icon.select同时为两个平台定义,以便未使用的一侧可以按平台被 tree-shake 掉。 title仅在 iOS 上会渲染为分组标题;Android 的 MaterialDropdownMenu没有 title 插槽。- 在 Android 上,
MenuView会用自己的Pressable包裹触发器以打开菜单,因此你传入的children上附加的onPress/onLongPress处理函数不会触发——外层包装会抢占该手势。请把该处理函数移到你的onPressAction分支逻辑中,或者如果你需要在触发器上保留单击和长按的不同动作,请使用更底层的DropdownMenu基础组件。 - 命令式的
ref.show()API 仅限 Android。SwiftUI 的Menu/ContextMenu没有程序化打开 API,所以在 iOS 上该调用不会生效(并会有一次性的开发警告)。 - 以下来自
@react-native-menu/menu的属性不受支持:themeVariant、hitSlop、isAnchoredToRight、subtitle、keepsMenuPresented、preferredElementSize,以及state: 'mixed'。
基本用法
将任意视图作为触发器。MenuView 会自动附加自身的点击或长按处理。
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Text, View } from 'react-native'; const editIcon = Icon.select({ ios: 'pencil', android: import('@expo/material-symbols/edit.xml'), }); const deleteIcon = Icon.select({ ios: 'trash', android: import('@expo/material-symbols/delete.xml'), }); export default function MenuExample() { return ( <MenuView actions={[ { id: 'edit', title: '编辑', image: editIcon }, { id: 'delete', title: '删除', image: deleteIcon, attributes: { destructive: true } }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <View> <Text>打开菜单</Text> </View> </MenuView> ); }
长按(上下文菜单)
将 shouldOpenOnLongPress 设置为以上下文菜单形式渲染。在 Android 上,同一个受控的 DropdownMenu 会在长按触发器时打开,而不是点击。在 iOS 上,这会使用 SwiftUI 的 ContextMenu,并将触发器显示为模糊预览。
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Text, View } from 'react-native'; const copyIcon = Icon.select({ ios: 'doc.on.doc', android: import('@expo/material-symbols/content_copy.xml'), }); const shareIcon = Icon.select({ ios: 'square.and.arrow.up', android: import('@expo/material-symbols/share.xml'), }); export default function LongPressMenuExample() { return ( <MenuView shouldOpenOnLongPress actions={[ { id: 'copy', title: '复制', image: copyIcon }, { id: 'share', title: '分享', image: shareIcon }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <View> <Text>长按我</Text> </View> </MenuView> ); }
子菜单和内联分组
subactions 默认会将嵌套操作渲染为子菜单。将父项上的 displayInline: true 可改为将子项作为内联分组渲染,这对于分组很有用。在 Android 上,只会显示分隔线(Material 的 DropdownMenu 没有 section 基础组件)。在 iOS 上,父项的 title 会变成分组标题。
import { MenuView } from '@expo/ui/community/menu'; import { Text, View } from 'react-native'; export default function SubmenuExample() { return ( <MenuView actions={[ { id: 'rename', title: '重命名' }, { id: 'sort', title: '排序方式', subactions: [ { id: 'sort-name', title: '名称' }, { id: 'sort-date', title: '日期' }, { id: 'sort-size', title: '大小' }, ], }, { id: 'share-section', title: '分享', displayInline: true, subactions: [ { id: 'share-airdrop', title: 'AirDrop' }, { id: 'share-message', title: '信息' }, ], }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <View> <Text>打开菜单</Text> </View> </MenuView> ); }
带勾选标记的切换项
将 state 设置为 'on' 或 'off',即可将操作渲染为可切换项;当为 on 时会在前面显示勾选标记。选择该操作会触发 onPressAction,由调用方负责更新状态。
import { MenuView } from '@expo/ui/community/menu'; import { useState } from 'react'; import { Text, View } from 'react-native'; export default function ToggleMenuExample() { const [pinned, setPinned] = useState(false); return ( <MenuView actions={[{ id: 'pin', title: '置顶', state: pinned ? 'on' : 'off' }]} onPressAction={e => { if (e.nativeEvent.event === 'pin') setPinned(p => !p); }}> <View> <Text>{pinned ? '已置顶' : '未置顶'}</Text> </View> </MenuView> ); }
API
import { MenuView } from '@expo/ui/community/menu';
Component
Type: React.Element<MenuComponentProps & {
ref: Ref<MenuComponentRef>
}>
A drop-in replacement for @react-native-menu/menu's MenuView. Wrap any trigger
view; long-pressing or tapping (per shouldOpenOnLongPress) shows a popup menu
built from the actions tree.
- On Android, renders via Compose's
DropdownMenuanchored to aPressable. - On iOS, renders via SwiftUI's
Menu(tap) orContextMenu(long-press). - On web, the trigger renders the trigger but actions do not fire;
a one-time
console.warnis emitted.
Props
ReactNodeTrigger view. Long-pressing or tapping (per shouldOpenOnLongPress) opens the menu.
ColorSchemeNameThe color scheme of the menu on Android. Omit to follow the device setting.
() => voidCallback invoked when the menu closes (either via dismissal or after an action fires).
On Android, fires from the controlled DropdownMenu's dismiss path.
On iOS, SwiftUI Menu/ContextMenu do not expose a close hook in a way we can
forward, so this is not fired there.
() => voidCallback invoked when the menu opens.
On Android, fires when the trigger's tap/long-press flips expanded to true.
On iOS, SwiftUI Menu/ContextMenu do not expose an open hook, so this is not
fired there.
(event: NativeActionEvent) => voidCallback invoked when a menu action is selected.
boolean • Default: falseWhen true, the menu opens on long-press of the trigger instead of a single tap.
Types
A single action inside a MenuView.
Compatible with @react-native-menu/menu.
| Property | Type | Description |
|---|---|---|
| attributes(optional) | MenuAttributes | Visual/behavioral flags. |
| displayInline(optional) | boolean | When |
| id(optional) | string | Identifier passed back via |
| image(optional) | SFSymbol | ImageSourcePropType | Icon to render beside the action label.
|
| imageColor(optional) | ColorValue | Tint color applied to the action's icon. Visually applied on Android via the leading |
| state(optional) | MenuState | Selection state. When |
| subactions(optional) | MenuAction[] | Nested actions. Without |
| title | string | Action label shown in the menu. |
| titleColor(optional) | ColorValue | Only for: Android Text color of the action label. |
Visual and behavioral attributes of a menu action.
Compatible with @react-native-menu/menu.
| Property | Type | Description |
|---|---|---|
| destructive(optional) | boolean | Renders the action with a destructive style (red text/icon). |
| disabled(optional) | boolean | Disables the action so it can't be activated. |
| hidden(optional) | boolean | Hides the action from the menu. |
Imperative handle exposed by MenuView via ref.
Compatible with @react-native-menu/menu's ref.show() API.
| Property | Type | Description |
|---|---|---|
| show | () => void | Only for: Android Programmatically open the menu. On Android, opens the anchored |
Literal type: string
Selection state for a menu action.
'on' renders a checkmark; 'off' doesn't.
Acceptable values are: 'on' | 'off'