模态框
编辑页面
了解如何在 Expo Router 中使用模态框。
For the complete documentation index, see llms.txt. Use this Use this file to discover all available pages.

了解如何在应用的其余部分之上显示内容的不同方式。
模态窗口是移动应用中常见的用户界面模式。它们用于在现有屏幕之上展示内容,并可用于不同目的,例如显示确认提示或独立表单。你可以使用以下方法在应用中创建模态窗口:
- 使用 React Native 的
Modal组件。 - 使用 Expo Router 特殊的基于文件的语法,在应用的导航系统中创建一个模态屏幕。
每种方式都有其特定的使用场景。了解何时使用哪种方法,对于创造良好的用户体验非常重要。
React Native 的 Modal 组件
Modal 组件是 React Native 核心 API 的一部分。常见使用场景包括:
- 独立交互,例如不需要成为导航系统一部分的自包含任务。
- 临时提示或确认对话框,适合快速交互。
下面是一个自定义 Modal 组件的示例,它会在不同平台上覆盖当前屏幕:
对于大多数使用场景,你都可以使用 Modal 组件,并根据应用的用户界面需求进行自定义。有关如何使用 Modal 组件及其 props 的详细信息,请参阅 React Native 文档。
使用 Expo Router 的模态屏幕
模态屏幕是在 src/app 目录中创建的文件,并作为现有堆栈中的一个路由使用。它适用于需要成为导航系统一部分的复杂交互,例如多步骤表单,在流程完成后你可以链接到特定屏幕。
下面是模态屏幕在不同平台上如何工作的示例:
用法
要实现模态路由,请在 src/app 目录中创建一个名为 modal.tsx 的屏幕。下面是一个示例文件结构:
srcapp_layout.tsxindex.tsxmodal.tsx上述文件结构会生成一种布局,其中 index 是堆栈中的第一个路由。在根布局文件(src/app/_layout.tsx)中,你可以将 modal 路由添加到堆栈中。要将其呈现为模态窗口,请将该路由的 presentation 选项设置为 modal。
import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" /> <Stack.Screen name="modal" options={{ presentation: 'modal', }} /> </Stack> ); }
你可以使用 Link 组件从 index.tsx 文件导航到模态屏幕。
import { Link } from 'expo-router'; import { StyleSheet, Text, View } from 'react-native'; export default function Home() { return ( <View style={styles.container}> <Text>首页屏幕</Text> <Link href="/modal" style={styles.link}> 打开模态框 </Link> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, link: { paddingTop: 20, fontSize: 20, }, });
modal.tsx 显示模态框的内容。
import { StyleSheet, Text, View } from 'react-native'; export default function Modal() { return ( <View style={styles.container}> <Text>模态屏幕</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
模态框的呈现与关闭行为
当模态框作为导航器中的当前屏幕并以独立屏幕的形式呈现时,它会失去之前的上下文。其呈现和关闭行为在不同平台上有所不同:
- 在 Android 上,模态框会从当前屏幕上方滑入。要关闭它,请使用返回按钮返回到前一个屏幕。
- 在 iOS 上,模态框会从当前屏幕底部滑入。要关闭它,请从顶部向下滑动。
- 在 web 上,模态框会作为一个单独的路由呈现,关闭行为必须使用
router.canGoBack()手动提供。以下是关闭模态框的示例:
import { Link, router} from 'expo-router'; import { StyleSheet, Text, View } from 'react-native'; export default function Modal() { const isPresented = router.canGoBack(); return ( <View style={styles.container}> <Text>模态框屏幕</Text> {isPresented && <Link href="../">关闭模态框</Link>} </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
在 iOS 上更改状态栏外观
默认情况下,在 iOS 上,模态框具有深色背景,这会隐藏状态栏。要更改状态栏外观,你可以使用 Platform API 检查当前平台是否为 iOS,然后在 modal.tsx 文件中使用 StatusBar 组件来更改外观。
import { StyleSheet, Text, View, Platform } from 'react-native'; import { StatusBar } from 'expo-status-bar'; export default function Modal() { return ( <View style={styles.container}> <Text>模态屏幕</Text> <StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
处理深度链接模态框
在使用堆栈或嵌套堆栈导航器时,模态框需要进行锚定以确保正确的导航行为。当深度链接到模态路由时,这一点尤为重要。如果不进行锚定,模态框后面的屏幕会被清除,从而失去导航上下文。
锚点 作为模态框的基础。在复杂应用中,当你有嵌套堆栈时,必须为嵌套堆栈定义锚点,并且它的值会成为该堆栈的初始路由。
你可以通过从堆栈的布局文件中导出 unstable_settings 来配置锚点:
export const unstable_settings = { anchor: 'index', // 锚定到 index 路由 };
在上面的示例中,anchor: 'index' 告诉 Expo Router 在展示模态框时,应在后台保留指定的锚点路由。
表单式片段展示
表单式片段将模态框作为底部抽屉呈现,应用用户可以在不同高度之间拖动它(称为 detent)。这对于需要部分屏幕覆盖且支持交互式调整大小的内容非常有用。
基本用法
要使用 form sheet,请在模态屏幕上将 presentation 选项设置为 formSheet:
import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" /> <Stack.Screen name="modal" options={{ presentation: 'formSheet', }} /> </Stack> ); }
配置片段 detent
Detent 定义了片段可以停留的高度。使用 sheetAllowedDetents 来配置它们:
-
数值数组 (
number[]): 将吸附位置指定为屏幕高度 0 到 1 之间的分数。例如,[0.25, 0.5, 1]会在 25%、50% 和全屏高度处创建三个吸附点。值必须按升序排序。 -
适应内容 (
'fitToContents'): 片段会根据其内容自动调整大小。使用此选项时,你必须提供明确的内容尺寸,因为不支持flex: 1,这是因为片段需要知道内容的实际大小来确定其高度。
Android 最多支持 3 个 detent。iOS 接受任意数量的 detent。
import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" /> <Stack.Screen name="modal" options={{ presentation: 'formSheet', sheetAllowedDetents: [0.25, 0.5, 1], sheetInitialDetentIndex: 1, }} /> </Stack> ); }
其他片段选项
| 选项 | 类型 | 描述 |
|---|---|---|
sheetInitialDetentIndex | number | 'last' | 片段打开时所在的 detent 索引(默认值:0)。 |
sheetGrabberVisible | boolean | 在片段顶部显示一个拖拽手柄(仅 iOS)。 |
sheetCornerRadius | number | 片段的圆角半径,单位为像素。 |
sheetLargestUndimmedDetentIndex | number | 'none' | 'last' | 保持背景不变暗的最大 detent 索引。 |
import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" /> <Stack.Screen name="modal" options={{ presentation: 'formSheet', sheetAllowedDetents: [0.25, 0.5, 1], sheetInitialDetentIndex: 0, sheetGrabberVisible: true, sheetCornerRadius: 24, sheetLargestUndimmedDetentIndex: 1, }} /> </Stack> ); }
片段底部栏(Android)
unstable_sheetFooter是仅限 Android 的 experimental 功能,未来版本中可能会发生变化。
你可以使用 React 组件为片段添加一个在所有 detent 位置都保持可见的底部栏:
import { Stack } from 'expo-router'; import { View, Button } from 'react-native'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" /> <Stack.Screen name="modal" options={{ presentation: 'formSheet', sheetAllowedDetents: [0.5, 1], unstable_sheetFooter: () => ( <View style={{ padding: 16, backgroundColor: 'white' }}> <Button title="Confirm" onPress={() => {}} /> </View> ), }} /> </Stack> ); }
在自定义 detent 中使用 flex: 1
在 SDK 55 及更高版本中,当使用自定义数值 detent 时,iOS 上的flex: 1可以正常工作。这在fitToContents模式下不适用,在该模式下你必须提供明确的内容尺寸。
当使用数值 detent 时,你的模态内容可以使用 flex: 1 来填充片段内可用空间:
import { StyleSheet, Text, View } from 'react-native'; export default function Modal() { return ( <View style={styles.container}> <Text>模态内容</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'white', }, });
其他信息
展示选项
在 Android 和 iOS 上,可以使用 presentation 选项以不同方式展示模态屏幕。
| 选项 | 描述 |
|---|---|
card | 新屏幕将被推入堆栈。Android 上的默认动画会因 OS 版本和主题而异。iOS 上,它将从侧边滑入。 |
modal | 新屏幕将以模态方式展示,允许在屏幕内渲染嵌套堆栈。 |
transparentModal | 新屏幕将以模态方式展示,同时保留之前的屏幕可见。如果屏幕具有半透明背景,则仍然可以看到下方内容。 |
containedModal | 在 Android 上,回退为 modal。在 iOS 上,使用 UIModalPresentationCurrentContext 模态样式。 |
containedTransparentModal | 在 Android 上,回退为 transparentModal。在 iOS 上,使用 UIModalPresentationOverCurrentContext 模态样式。 |
fullScreenModal | 在 Android 上,回退为 modal。在 iOS 上,使用 UIModalPresentationFullScreen 模态样式。 |
formSheet | 展示一个带有可配置 detent 的底部抽屉。详情参见 FormSheet presentation。 |