This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 56).
菜单
与 @react-native-menu/menu 兼容的菜单。
For the complete documentation index, see llms.txt. Use this file to discover all available pages.
一个与 @react-native-menu/menu 的 API 兼容的 MenuView 组件。支持单击(默认)和长按(shouldOpenOnLongPress)两种触发方式。
在内部,此组件封装了平台特定的 @expo/ui 基础组件:
- Android:Jetpack Compose DropdownMenu,锚定到一个
Pressable触发器。 - iOS:SwiftUI Menu 用于点击触发,SwiftUI ContextMenu 用于长按触发。
如果你需要更底层的控制,请直接使用这些基础组件。
安装
- 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'。
基本用法
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: '编辑', image: editIcon }, { id: 'delete', title: '删除', image: deleteIcon, attributes: { destructive: true } }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>打开菜单</Text> </Pressable> </MenuView> ); }
长按(上下文菜单)
设置 shouldOpenOnLongPress 可将其渲染为上下文菜单。在 Android 上,同一个受控的 DropdownMenu 会通过 Pressable 的 onLongPress 而不是 onPress 打开。在 iOS 上,这会使用 SwiftUI 的 ContextMenu,并将触发器显示为模糊预览。
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: '复制', image: copyIcon }, { id: 'share', title: '分享', image: shareIcon }, ]} onPressAction={e => console.log(e.nativeEvent.event)}> <Pressable> <Text>长按我</Text> </Pressable> </MenuView> ); }
子菜单和内联分组
subactions 默认会将嵌套操作渲染为子菜单。将父项上的 displayInline: true 可改为将子项作为内联分组渲染,这对于分组很有用。在 Android 上,只会显示分隔线(Material 的 DropdownMenu 没有 section 基础组件)。在 iOS 上,父项的 title 会变成分组标题。
import { MenuView } from '@expo/ui/community/menu'; import { Pressable, Text } 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)}> <Pressable> <Text>打开菜单</Text> </Pressable> </MenuView> ); }
带勾选标记的切换项
将 state 设置为 'on' 或 'off',即可将操作渲染为可切换项;当为 on 时会在前面显示勾选标记。选择该操作会触发 onPressAction,由调用方负责更新状态。
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: '置顶', state: pinned ? 'on' : 'off' }]} onPressAction={e => { if (e.nativeEvent.event === 'pin') setPinned(p => !p); }}> <Pressable> <Text>{pinned ? '已置顶' : '未置顶'}</Text> </Pressable> </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.
() => 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'