ModalBottomSheet
一个 Jetpack Compose ModalBottomSheet 组件,从屏幕底部渲染内容。
For the complete documentation index, see llms.txt. Use this file to discover all available pages.
Expo UI ModalBottomSheet 与官方的 Jetpack Compose Bottom Sheet API 匹配,并在从底部滑出的模态面板中显示内容。
🌐 Expo UI ModalBottomSheet matches the official Jetpack Compose Bottom Sheet API and displays content in a modal sheet that slides up from the bottom.
安装
🌐 Installation
- npx expo install @expo/uiIf you are installing this in an existing React Native app, make sure to install expo in your project.
用法
🌐 Usage
基本底部面板
🌐 Basic bottom sheet
使用 ref.hide() 在卸载前通过动画以编程方式关闭该面板。
🌐 Use ref.hide() to programmatically dismiss the sheet with an animation before unmounting it.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; export default function BasicBottomSheetExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)}> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[paddingAll(24)]}> <Text>Hello from bottom sheet!</Text> <Text>You can add more content here.</Text> <Button onClick={hideSheet}> <Text>Close</Text> </Button> </Column> </ModalBottomSheet> )} </Host> ); }
跳过部分展开状态
🌐 Skip partially expanded state
当设置 skipPartiallyExpanded 时,表单会直接以完全展开的状态打开,而不是先停在半高位置。
🌐 When skipPartiallyExpanded is set, the sheet opens directly in the fully expanded state instead of stopping at the half-height position first.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; export default function SkipPartiallyExpandedExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} skipPartiallyExpanded> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[paddingAll(24)]}> <Text>This sheet skips the partially expanded state.</Text> <Text>It opens directly in the fully expanded position.</Text> <Button onClick={hideSheet}> <Text>Close</Text> </Button> </Column> </ModalBottomSheet> )} </Host> ); }
自定义颜色
🌐 Custom colors
使用 containerColor、contentColor 和 scrimColor 来自定义工作表的外观。
🌐 Use containerColor, contentColor, and scrimColor to customize the sheet's appearance.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; export default function CustomColorsExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Colored Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} containerColor="#1a1a2e" contentColor="#e0e0e0" scrimColor="#806200EE"> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[paddingAll(24)]}> <Text>Custom styled bottom sheet.</Text> <Text>Dark container with a purple scrim overlay.</Text> <Button onClick={hideSheet}> <Text>Close</Text> </Button> </Column> </ModalBottomSheet> )} </Host> ); }
自定义拖动句柄
🌐 Custom drag handle
使用 ModalBottomSheet.DragHandle 插槽提供自定义拖动句柄,或设置 showDragHandle={false} 完全隐藏它。
🌐 Use ModalBottomSheet.DragHandle slot to provide a custom drag handle, or set showDragHandle={false} to hide it entirely.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, Box, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { background, clip, fillMaxWidth, height, padding, Shapes, width, } from '@expo/ui/jetpack-compose/modifiers'; export default function CustomDragHandleExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Custom Handle Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)}> <ModalBottomSheet.DragHandle> <Column horizontalAlignment="center" modifiers={[fillMaxWidth(), padding(0, 12, 0, 8)]}> <Box modifiers={[width(60), height(6), clip(Shapes.Circle), background('#6200EE')]} /> </Column> </ModalBottomSheet.DragHandle> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[padding(16, 16, 16, 16)]}> <Button onClick={hideSheet}> <Text>Close</Text> </Button> </Column> </ModalBottomSheet> )} </Host> ); }
底部弹出层中的 React Native 内容
🌐 React Native content inside a bottom sheet
使用 RNHostView 在 Compose 底部弹出窗中嵌入交互式的 React Native 视图。这让你可以将 Compose 布局与 RN 组件如 Pressable 和 Text 混合使用。
🌐 Use RNHostView to embed interactive React Native views inside a Compose bottom sheet. This lets you mix Compose layout with RN components like Pressable and Text.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { padding } from '@expo/ui/jetpack-compose/modifiers'; import { Pressable, Text as RNText, View } from 'react-native'; export default function RNContentBottomSheetExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open RN Content Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} skipPartiallyExpanded={false}> <Column verticalArrangement={{ spacedBy: 16 }} modifiers={[padding(16, 16, 16, 16)]}> <Text>Mixing Compose + RN in a Bottom Sheet</Text> <RNHostView matchContents> <View> <RNText style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 8 }}> React Native Content </RNText> <Pressable style={{ backgroundColor: '#007AFF', padding: 12, borderRadius: 8, alignItems: 'center', }} onPress={hideSheet}> <RNText style={{ color: 'white', fontWeight: '600' }}>Close</RNText> </Pressable> </View> </RNHostView> </Column> </ModalBottomSheet> )} </Host> ); }
带有 flex 的 React Native 内容
🌐 React Native content with flex
在不使用 matchContents 的情况下使用 RNHostView,让 RN 视图填充表单内部的剩余空间。与在父 Column 上使用固定的 height 修饰符结合,以控制表单的大小。
🌐 Use RNHostView without matchContents to let the RN view fill the remaining space inside the sheet. Combine with a fixed height modifier on the parent Column to control the sheet size.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, RNHostView, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { height, padding } from '@expo/ui/jetpack-compose/modifiers'; import { Text as RNText, View } from 'react-native'; export default function FlexRNContentExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Flex Content Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} skipPartiallyExpanded> <Column modifiers={[height(400), padding(16, 16, 16, 16)]}> <Text>RN View with flex: 1</Text> <RNHostView> <View style={{ flex: 1, backgroundColor: '#9B59B6', borderRadius: 10 }}> <RNText style={{ color: 'white', fontSize: 18, fontWeight: 'bold', padding: 16, }}> React Native Content (flex: 1) </RNText> </View> </RNHostView> </Column> </ModalBottomSheet> )} </Host> ); }
不可取消的面板
🌐 Non-dismissible sheet
将 properties、sheetGesturesEnabled 结合起来创建一个只能通过编程方式关闭的表单。
🌐 Combine properties, sheetGesturesEnabled to create a sheet that can only be closed programmatically.
import { useRef, useState } from 'react'; import { Host, ModalBottomSheet, Button, Column, Text } from '@expo/ui/jetpack-compose'; import type { ModalBottomSheetRef } from '@expo/ui/jetpack-compose'; import { paddingAll } from '@expo/ui/jetpack-compose/modifiers'; export default function NonDismissibleExample() { const [visible, setVisible] = useState(false); const sheetRef = useRef<ModalBottomSheetRef>(null); const hideSheet = async () => { await sheetRef.current?.hide(); setVisible(false); }; return ( <Host matchContents> <Button onClick={() => setVisible(true)}> <Text>Open Non-Dismissible Sheet</Text> </Button> {visible && ( <ModalBottomSheet ref={sheetRef} onDismissRequest={() => setVisible(false)} sheetGesturesEnabled={false} properties={{ shouldDismissOnBackPress: false, shouldDismissOnClickOutside: false, }}> <Column verticalArrangement={{ spacedBy: 12 }} modifiers={[paddingAll(24)]}> <Text>This sheet cannot be dismissed by swiping, back press, or tapping outside.</Text> <Text>Only the button below will close it.</Text> <Button onClick={hideSheet}> <Text>Close</Text> </Button> </Column> </ModalBottomSheet> )} </Host> ); }
应用接口
🌐 API
import { ModalBottomSheet } from '@expo/ui/jetpack-compose';
Constants
Props
React.ReactNodeThe children of the ModalBottomSheet component.
Can include a ModalBottomSheet.DragHandle slot for a custom drag handle.
ColorValueThe preferred color of the content inside the bottom sheet.
() => voidCallback function that is called when the user dismisses the bottom sheet (via swipe, back press, or tapping outside the scrim).
ModalBottomSheetPropertiesProperties for the modal window behavior.
Ref<ModalBottomSheetRef>Can be used to imperatively hide the bottom sheet with an animation.
boolean • Default: trueWhether gestures (swipe to dismiss) are enabled on the bottom sheet.
boolean • Default: trueWhether to show the default drag handle at the top of the bottom sheet.
Ignored if a custom ModalBottomSheet.DragHandle slot is provided.
Types
| Property | Type | Description |
|---|---|---|
| shouldDismissOnBackPress(optional) | boolean | Whether the bottom sheet can be dismissed by pressing the back button. Default: true |
| shouldDismissOnClickOutside(optional) | boolean | Whether the bottom sheet can be dismissed by clicking outside (on the scrim). Default: true |
| Property | Type | Description |
|---|---|---|
| hide | () => Promise<void> | Programmatically hides the bottom sheet with an animation. The returned promise resolves after the dismiss animation completes. |