Reference version

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

Expo Router Split View

一个 Expo Router 子模块,提供原生分屏布局。

iOS
Recommended version:
~58.0.2

expo-router/unstable-split-view 是 expo-router 的子模块,导出用于构建分栏布局的组件,这些布局使用平台原生系统分栏视图。

平台支持

Split View 仅适用于 iOS。在其他平台上,SplitView 组件会自动回退为标准 Slot 导航器,确保你的应用无需条件代码即可在所有平台上运行。

iPhone 支持

在 iPhone 上,SplitView 会自动将所有列折叠为单个视图。一次仅显示一列。

选择初始列

使用 topColumnForCollapsing 属性控制分栏视图折叠时显示哪一列:

<SplitView topColumnForCollapsing="primary">{/* ... */}</SplitView>

接受的值为 primary、supplementary 和 secondary。未设置时,系统将使用其默认行为。

在列之间导航

使用 ref 和 show 方法以编程方式显示特定列:

import { useRef } from 'react'; import { Pressable, Text } from 'react-native'; import { SplitView } from 'expo-router/unstable-split-view'; import type { SplitHostCommands } from 'react-native-screens/experimental'; export default function Layout() { const ref = useRef<SplitHostCommands>(null); return ( <SplitView ref={ref} topColumnForCollapsing="primary"> <SplitView.Column> <Pressable onPress={() => ref.current?.show('secondary')}> <Text>Show main content</Text> </Pressable> </SplitView.Column> </SplitView> ); }

已知限制

Cannot be nested

导航层级中只能有一个 SplitView。尝试嵌套分栏视图将导致错误。

Cannot be used inside other navigators

SplitView 不能放在其他导航器(Slot 除外)中使用。它必须位于根布局层级。

Only specific children allowed

SplitView 仅接受 SplitView.Column 和 SplitView.Inspector 作为直接子项。其他组件将被忽略,并显示警告。

Header cannot be customized yet

分栏视图列中的标题栏(导航栏)目前无法自定义。暂不支持自定义标题栏配置。

Limited API

当前 API 接口有限,可能无法涵盖所有用例。未来版本将添加更多属性和配置选项。

Going back to previous column on iPhone

若要返回上一列,请点按导航栏中的系统返回按钮。未来版本将添加更精细的返回导航编程控制功能。

安装

若要在项目中使用 expo-router/unstable-split-view,需要在项目中安装 expo-router。请按照 Expo Router 安装指南中的说明操作:

Install Expo Router

Learn how to install Expo Router in your project.

使用 SplitView.Column

SplitView.Column 用于在分栏布局中定义其他列。在主要内容区域之前最多可以添加两列。

两列布局

包含主要内容的简单侧边栏:

app/_layout.tsx
import { Link } from 'expo-router'; import { SplitView } from 'expo-router/unstable-split-view'; import { Text, Pressable } from 'react-native'; import { SafeAreaView } from 'react-native-screens/experimental'; export default function Layout() { return ( <SplitView> <SplitView.Column> <SafeAreaView edges={{ left: true, top: true }} style={{ flex: 1 }}> <Link href="/inbox"> <Pressable style={{ padding: 16 }}> <Text>Inbox</Text> </Pressable> </Link> <Link href="/sent"> <Pressable style={{ padding: 16 }}> <Text>Sent</Text> </Pressable> </Link> </SafeAreaView> </SplitView.Column> </SplitView> ); }

三列布局

包含辅助列和主要内容的侧边栏:

app/_layout.tsx
import { Link, useGlobalSearchParams } from 'expo-router'; import { SplitView } from 'expo-router/unstable-split-view'; import { SafeAreaView } from 'react-native-screens/experimental'; export default function Layout() { const params = useGlobalSearchParams(); return ( <SplitView> <SplitView.Column> <SafeAreaView edges={{ left: true, top: true }} style={{ flex: 1, gap: 16, padding: 16, }}> <Link href="/?col1=1" style={{ fontWeight: params.col1 === '1' ? 'bold' : 'normal' }}> Option 1 </Link> <Link href="/?col1=2" style={{ fontWeight: params.col1 === '2' ? 'bold' : 'normal' }}> Option 2 </Link> <Link href="/?col1=3" style={{ fontWeight: params.col1 === '3' ? 'bold' : 'normal' }}> Option 3 </Link> </SafeAreaView> </SplitView.Column> <SplitView.Column> <SafeAreaView edges={{ left: true, top: true }} style={{ flex: 1, gap: 16, padding: 16, }}> <Link href={`/?col1=${params.col1}&col2=1`}>Sub-Option 1</Link> <Link href={`/?col1=${params.col1}&col2=2`}>Sub-Option 2</Link> <Link href={`/?col1=${params.col1}&col2=3`}>Sub-Option 3</Link> </SafeAreaView> </SplitView.Column> </SplitView> ); }

使用 SplitView.Inspector

SplitView.Inspector 会添加一个从尾随边缘滑入的辅助列,适合显示其他详细信息或元数据:

<SplitView> <SplitView.Column>{/* Sidebar */}</SplitView.Column> <SplitView.Inspector> <View style={{ flex: 1, padding: 16 }}> <Text>Inspector Panel</Text> </View> </SplitView.Inspector> </SplitView>

完整示例

下面是一个具有三列的密码管理器风格应用:

app
 _layout.tsx
 index.tsx
 [type]
  [id].tsx
  index.tsx
app/_layout.tsx
import { Link, Color, useGlobalSearchParams } from 'expo-router'; import { SplitView } from 'expo-router/unstable-split-view'; import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { SafeAreaView } from 'react-native-screens/experimental'; export default function Layout() { return ( <SplitView showInspector> <SplitView.Column> <PasscodeList /> </SplitView.Column> <SplitView.Column> <PasswordElementList /> </SplitView.Column> <SplitView.Inspector> <InspectorContent /> </SplitView.Inspector> </SplitView> ); } function PasscodeList() { return ( <SafeAreaView edges={{ top: true, left: true }} style={style.passcodeList}> <PasscodeCard title="All" param="all" /> <PasscodeCard title="Passkeys" param="passkeys" /> <PasscodeCard title="Codes" param="codes" /> <PasscodeCard title="Security" param="security" /> <PasscodeCard title="Deleted" param="deleted" /> </SafeAreaView> ); } const passkeys = ['Github', 'Google', 'Facebook', 'Twitter', 'Apple', 'Microsoft', 'Amazon']; const security = ['Admin1234', 'Root']; const all = [...passkeys, ...security]; function PasswordElementList() { const params = useGlobalSearchParams(); const data = (() => { switch (params.type) { case 'all': case undefined: return all; case 'passkeys': return passkeys; case 'security': return security; default: return []; } })(); return ( <ScrollView contentInsetAdjustmentBehavior="automatic" style={{ backgroundColor: undefined }}> {data.map(item => ( <PasswordElement key={item} title={item} /> ))} </ScrollView> ); } function PasscodeCard({ param, title }: { param: string; title: string }) { const params = useGlobalSearchParams(); const isActive = params.type === param; return ( <Link href={`/${param}/`} disabled={isActive} style={[ style.passcodeCard, { backgroundColor: isActive ? Color.ios.systemBlue : Color.ios.systemGray6, }, ]} asChild> <Pressable> <Text style={{ color: isActive ? 'white' : 'black', fontSize: 16 }}>{title}</Text> </Pressable> </Link> ); } function PasswordElement({ title }: { title: string }) { const params = useGlobalSearchParams(); const isActive = params.id === title; return ( <Link href={`/${params.type}/${title}/`} asChild> <Pressable style={{ backgroundColor: isActive ? Color.ios.systemBlue : undefined, padding: 12, }}> <SafeAreaView edges={{ left: true }}> <Text style={{ color: isActive ? 'white' : 'black', fontSize: 16 }}>{title}</Text> </SafeAreaView> </Pressable> </Link> ); } function InspectorContent() { return ( <View style={style.inspectorContent}> <Text>Inspector</Text> </View> ); } const style = StyleSheet.create({ passcodeList: { flex: 1, flexWrap: 'wrap', gap: 8, flexDirection: 'row', padding: 8, }, passcodeCard: { width: '48%', padding: 12, borderRadius: 12, justifyContent: 'center', alignItems: 'center', height: 50, }, inspectorContent: { flex: 1, justifyContent: 'center', alignItems: 'center', }, });
app/index.tsx
import { Redirect } from 'expo-router'; export default function Index() { return <Redirect href="/all/" />; }
app/[type]/index.tsx
import { Color } from 'expo-router'; import { Text, View } from 'react-native'; export default function Index() { return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text style={{ color: Color.ios.label, fontSize: 24, fontWeight: 'bold' }}> Nothing is selected </Text> </View> ); }
app/[type]/[id].tsx
import { useLocalSearchParams } from 'expo-router'; import { Text, View } from 'react-native'; export default function Id() { const { id } = useLocalSearchParams(); return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>ID: {id}</Text> </View> ); }

API

import { SplitView } from 'expo-router/unstable-split-view';

Components

SplitView

iOS

Type: React.Element<SplitViewProps>

For full list of supported props, see SplitHostProps

SplitViewProps

activityEnabled

iOS
Optional • Type: boolean • Default: false

Enables React Activity for screens rendered by the detail slot.

children

iOS
Optional • Type: ReactNode

Inherited props

  • Omit<SplitHostProps, 'children'>

SplitView.Column

iOS

Type: React.Element<SplitViewColumnProps>

SplitViewColumnProps

children

iOS
Optional • Type: ReactNode

SplitView.Inspector

iOS 26+

Type: React.Element<SplitViewColumnProps>