之前因為沒時間好好研究 Expo Router,為了避免遇到bug所以一直使用react-navigation,這兩天剛好有時間嘗試expo-router差點沒被搞死(x
嵌套路由的返回
主要是卡在嵌套路由上,我試過這兩種路由結構:
第一種:
/app
├── settings.tsx
├── _layout.tsx
├── /basic
├── length.tsx
└── weight.tsx
└── home.tsx
第二種:
/app
├── settings.tsx
├── _layout.tsx
└── /home
├── /basic
├── length.tsx
└── weight.tsx
└── index.tsx
使用 Stack navigator,並且路由的跳轉都是使用 router.push,預期的行為是:
開啟APP 會在 home -> 進入 /basic/length -> 返回 /home -> 進入 /basic/weight -> 返回 /home -> 進入 /basic/temperature -> 返回 /home
但這兩種結構的實際行為都是這樣的:
開啟APP 會在 home -> 進入 /basic/length -> 返回 /home -> 進入 /basic/weight -> 返回 /basic/length -> 再返回才會到 /home -> 進入 /basic/temperature -> 返回 /basic/length > 再返回才會到 /home
總之它只會紀錄 /basic 內的頁面路由紀錄,直到 /basic 中沒有路由紀錄之後才會回到 /home。
解決辦法
最後的解決辦法是將 /basic 底下的內容全部扁平化,捨棄嵌套了。
/app
├── settings.tsx
├── _layout.tsx
└── /home
├── basic-length.tsx
├── basic-weight.tsx
├── _layout.tsx
└── index.tsx
所以說這算解決了嗎?好像也不算😂
Header的設置
這是第二困擾我的部分…直接把我的例子搬上來說明好了。
app/_layout.tsx
使用的是 Drawer navigator
<Drawer
screenOptions={drawerTheme}
drawerContent={(props) => <CustomDrawerContent props={props} />}
>
<Drawer.Screen
name="home"
options={{
title: '首頁',
drawerLabel: '首頁',
headerShown: true,
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>🏠</Text>
),
}}
/>
<Drawer.Screen
name="settings"
options={{
title: '設定',
drawerLabel: '設定',
headerShown: true,
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>⚙️</Text>
),
}}
/>
</Drawer>
app/settings/_layout.tsx
使用的是 Stack navigator
import { Stack } from 'expo-router'
export default function SettingsLayout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
title: '設定',
headerShown: false,
}}
/>
<Stack.Screen
name="favorite-tools"
options={{
title: '常用工具設置',
headerShown: false,
}}
/>
</Stack>
)
}
若將 settings 裡面的路由都設為 headerShown: false
,那麼在 /settings/favorite-tools 頁面時 Header 顯示的也會是「設定」,並且沒有返回按鈕,只有 Drawer 的按鈕。
但我希望 /settings/index 就顯示「設定」, /settings/favorite-tools 就顯示「常用工具設置」,並且要有返回按鈕。
改為 headerShown: true
<Stack.Screen
name="favorite-tools"
options={{
title: '常用工具設置',
headerShown: true,
}}
/>
又會出現雙重Header
最開始想到的做法是將 Drawer 的 header 隱藏,只顯示 Stack 的 Header,但這樣做的話會失去 Drawer 原本的側邊欄按鈕。
解決方法
如果想保持 Drawer 的側邊欄按鈕又要避免 Header 重複的話,我自己測試的結果是使用 Stack Header + 自定義drawer按鈕:
/settings/_layout.tsx
import { Ionicons } from '@expo/vector-icons'
import { DrawerActions } from '@react-navigation/native'
import { Stack, useNavigation } from 'expo-router'
import { Button } from 'tamagui'
export default function SettingsLayout() {
const navigation = useNavigation()
return (
<Stack>
<Stack.Screen
name="index"
options={{
title: '設定',
headerShown: true,
headerLeft: () => {
return (
<Button
style={{ backgroundColor: 'transparent' }}
p={10}
onPress={() => {
navigation.dispatch(DrawerActions.openDrawer())
}}
>
<Ionicons name="menu" size={26} color="black" />
</Button>
)
},
}}
/>
<Stack.Screen
name="favorite-tools"
options={{
title: '常用工具設置',
headerShown: true,
}}
/>
</Stack>
)
}
Drawer 就設為 headerShow: false
吧
<Drawer
screenOptions={drawerTheme}
drawerContent={(props) => <CustomDrawerContent props={props} />}
>
<Drawer.Screen
name="home"
options={{
drawerLabel: '首頁',
headerShown: true,
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>🏠</Text>
),
}}
/>
<Drawer.Screen
name="settings"
options={{
drawerLabel: '設定',
headerShown: true,
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>⚙️</Text>
),
}}
/>
</Drawer>
沒用的 initialRouteName
/app
├── settings.tsx
├── _layout.tsx
└── /home
├── basic-length.tsx
├── basic-weight.tsx
├── _layout.tsx
└── index.tsx
在 iOS 上設置 initialRouteName
沒有作用,會出現Unmatched Route,因為 iOS 會將 /index
作為首頁,不管有沒有 index 文件。
但在Android上即使不設置 initialRouteName
,一開啟APP也會自動將 /home/index
作為首頁:
<Drawer
screenOptions={drawerTheme}
initialRouteName="home"
drawerContent={(props) => <CustomDrawerContent props={props} />}
>
<Drawer.Screen
name="home"
options={{
title: '首頁',
headerShown: false,
drawerLabel: '首頁',
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>🏠</Text>
),
}}
/>
<Drawer.Screen
name="settings"
options={{
title: '設定',
headerShown: false,
drawerLabel: '設定',
drawerIcon: ({ color, size }) => (
<Text style={{ color, fontSize: size }}>⚙️</Text>
),
}}
/>
</Drawer>
解決辦法
在 app 底下新增 index.tsx,強制 Redirect 到指定的首頁:
import { Redirect } from "expo-router"
const Index = () => {
return <Redirect href="/home" />
}
export default Index