This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.

菜单

与 @react-native-menu/menu 兼容的菜单。

Android
iOS
Included in Expo Go
Recommended version:
~57.0.3

一个 MenuView 组件,其 API 与 @react-native-menu/menu 兼容。支持单击(默认)和长按(shouldOpenOnLongPress)触发。

🌐 A MenuView component with an API compatible with @react-native-menu/menu. Supports both single-tap (default) and long-press (shouldOpenOnLongPress) triggers.

在内部,这个组件封装了平台特定的 @expo/ui 原语:

🌐 Under the hood this component wraps the platform-specific @expo/ui primitives:

如果你需要更底层的控制,请直接使用那些原语。

🌐 If you need lower-level control, use those primitives directly.

安装

🌐 Installation

Terminal
npx expo install @expo/ui

If you are installing this in an existing React Native app, make sure to install expo in your project.

@react-native-menu/menu 迁移

🌐 Migrating from @react-native-menu/menu

  • 将导入从 import { MenuView } from '@react-native-menu/menu' 更新为 import { MenuView } from '@expo/ui/community/menu'
  • action.image 在 Android 上与上游不同。@react-native-menu/menu 期望一个 可绘制资源名称 字符串(例如 'ic_menu_add'),它会在 android/app/src/main/res/drawable/ 中解析。此替换组件解析可绘制资源名称——请改为传入 ImageSourcePropType(例如 require('@expo/material-symbols/edit.xml'))。在 iOS 上,字符串值可作为 SF Symbol 名称使用。使用 Icon.select 在每个调用位置定义两者,以便未使用的一侧在每个平台上被摇树优化掉。
  • title 仅在 iOS 上呈现为章节标题;Android 的 Material DropdownMenu 没有标题槽。
  • 在安卓上,MenuView 会将触发器封装在它自己的 Pressable 中以打开菜单,因此附加在你作为 children 传入的 Pressable 上的 onPress/onLongPress 处理器不会触发——外部封装器会占用手势。将该处理器改放到你的 onPressAction 切换中,或者如果你需要在触发器上保留独立的点击和长按动作,可以使用更底层的 DropdownMenu 原语。
  • 命令式 ref.show() API 仅限 Android。SwiftUI Menu/ContextMenu 没有可编程的开放 API,因此在 iOS 上调用不起作用(并会有一次性开发者警告)。
  • 以下来自 @react-native-menu/menu 的属性不受支持:themeVarianthitSlopisAnchoredToRightsubtitlekeepsMenuPresentedpreferredElementSizestate: 'mixed'

基本用法

🌐 Basic usage

MenuExample.tsx
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } 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: 'Edit', image: editIcon }, { id: 'delete', title: 'Delete', image: deleteIcon, attributes: { destructive: true } }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Open menu</Text> </Pressable> </MenuView> ); }

长按(上下文菜单)

🌐 Long-press (context menu)

shouldOpenOnLongPress 设置为呈现为上下文菜单。在 Android 上,相同的受控 DropdownMenuPressableonLongPress 打开,而不是从 onPress 打开。在 iOS 上,这使用 SwiftUI 的 ContextMenu 并将触发器显示为模糊预览。

🌐 Set shouldOpenOnLongPress to render as a context menu. On Android, the same controlled DropdownMenu opens from the Pressable's onLongPress instead of onPress. On iOS, this uses SwiftUI's ContextMenu and shows the trigger as a blurred preview.

LongPressMenuExample.tsx
import { Icon } from '@expo/ui'; import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } 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: 'Copy', image: copyIcon }, { id: 'share', title: 'Share', image: shareIcon }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Long-press me</Text> </Pressable> </MenuView> ); }

子菜单和内联部分

🌐 Submenus and inline sections

subactions 默认将嵌套操作呈现为子菜单。在父级上设置 displayInline: true 可以将子项呈现为内联部分,这对于分组很有用。在 Android 上,只有分隔线会显示(Material 的 DropdownMenu 没有部分原语)。在 iOS 上,父级的 title 会变成部分标题。

SubmenuExample.tsx
import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } from 'react-native'; export default function SubmenuExample() { return ( <MenuView actions={[ { id: 'rename', title: 'Rename' }, { id: 'sort', title: 'Sort by', subactions: [ { id: 'sort-name', title: 'Name' }, { id: 'sort-date', title: 'Date' }, { id: 'sort-size', title: 'Size' }, ], }, { id: 'share-section', title: 'Share', displayInline: true, subactions: [ { id: 'share-airdrop', title: 'AirDrop' }, { id: 'share-message', title: 'Message' }, ], }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>Open menu</Text> </Pressable> </MenuView> ); }

切换带有勾选的项目

🌐 Toggle items with checkmarks

state 设置为 'on''off',以便在开启时将某个操作呈现为可切换项目并带有前置勾选标记。选择该操作会触发 onPressAction,调用者负责更新状态。

🌐 Set state to 'on' or 'off' to render an action as a togglable item with a leading checkmark when on. Selecting the action fires onPressAction and the caller is responsible for updating the state.

ToggleMenuExample.tsx
import { MenuView } from '@expo/ui/community/menu'; import { useState } from 'react'; import { Pressable, Text } from 'react-native'; export default function ToggleMenuExample() { const [pinned, setPinned] = useState(false); return ( <MenuView actions={[{ id: 'pin', title: 'Pin to top', state: pinned ? 'on' : 'off' }]} onPressAction={e => { if (e.nativeEvent.event === 'pin') setPinned(p => !p); }}> <Pressable> <Text>{pinned ? 'Pinned' : 'Not pinned'}</Text> </Pressable> </MenuView> ); }

应用接口

🌐 API

import { MenuView } from '@expo/ui/community/menu';

Component

Only for:
Android
iOS

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 DropdownMenu anchored to a Pressable.
  • On iOS, renders via SwiftUI's Menu (tap) or ContextMenu (long-press).
  • On web, the trigger renders the trigger but actions do not fire; a one-time console.warn is emitted.

Props

actions

The actions to display in the menu.

children

Optional • Type: ReactNode

Trigger view. Long-pressing or tapping (per shouldOpenOnLongPress) opens the menu.

onCloseMenu

Only for:
Android

Optional • Type: () => void

Callback 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.

onOpenMenu

Only for:
Android

Optional • Type: () => void

Callback 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.

onPressAction

Optional • Type: (event: NativeActionEvent) => void

Callback invoked when a menu action is selected.

shouldOpenOnLongPress

Optional • Type: boolean • Default: false

When true, the menu opens on long-press of the trigger instead of a single tap.

style

Optional • Type: StyleProp<ViewStyle>

Style applied to the trigger wrapper.

testID

Optional • Type: string

Test identifier passed through to the trigger view.

title

Only for:
iOS

Optional • Type: string

Menu title shown at the top of the menu.

Types

A single action inside a MenuView. Compatible with @react-native-menu/menu.

PropertyTypeDescription
attributes(optional)MenuAttributes

Visual/behavioral flags.

displayInline(optional)boolean

When true and subactions is present, renders the children as an inline section inside the parent menu (with this action's title as the section header on iOS).

id(optional)string

Identifier passed back via onPressAction.nativeEvent.event when this action is selected. Defaults to title if omitted.

image(optional)SFSymbol | ImageSourcePropType

Icon to render beside the action label.

  • When an SFSymbol name (e.g. 'trash'), rendered on iOS only. Not rendered on Android — pass an ImageSourcePropType instead to show an icon there.
  • When an ImageSourcePropType (e.g. require('./trash.xml') or { uri: '...' }), rendered on Android via Compose Icon. Ignored on iOS; SwiftUI menus only accept SF Symbol names for built-in Menu/Button labels.
imageColor(optional)ColorValue

Tint color applied to the action's icon.

Visually applied on Android via the leading Icon's tint. On iOS, the value is accepted but may not render: SwiftUI's Menu/ContextMenu draw their items via the system menu UI, which ignores per-item color modifiers.

state(optional)MenuState

Selection state. When 'on', the action renders a checkmark.

subactions(optional)MenuAction[]

Nested actions. Without displayInline, renders as a submenu; with displayInline: true, renders as an inline section.

titlestring

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.

PropertyTypeDescription
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.

PropertyTypeDescription
show() => void
Only for:
Android

Programmatically open the menu.

On Android, opens the anchored DropdownMenu (equivalent to the user tapping the trigger). On iOS this is a no-op — SwiftUI Menu/ContextMenu have no programmatic open API; a one-time console.warn is emitted in development.

Literal Type: string

Selection state for a menu action. 'on' renders a checkmark; 'off' doesn't.

Acceptable values are: 'on' | 'off'

NativeActionEvent

Event payload delivered to onPressAction when an action is selected. Compatible with @react-native-menu/menu.

PropertyTypeDescription
nativeEvent{ event: string }
-