带嵌套导航器的屏幕选项
在本文件中,我们将解释当存在多个导航器时 屏幕选项 的工作原理。了解这一点很重要,以便您将 options
放置在正确的位置,并可以正确配置您的导航器。如果您将它们放在错误的位置,最好的情况是不会发生任何事情,最坏的情况是会发生一些令人困惑且意想不到的事情。
您只能从其屏幕组件之一修改导航器的导航选项。这同样适用于作为屏幕嵌套的导航器。
例如,让我们考虑一个包含每个标签中本地堆栈的标签导航器。如果我们在堆栈内的屏幕上设置 options
会发生什么?
const Tab = createTabNavigator();
const HomeStack = createNativeStackNavigator();
const SettingsStack = createNativeStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen
name="A"
component={A}
options={{ tabBarLabel: 'Home!' }}
/>
</HomeStack.Navigator>
);
}
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="B"
component={B}
options={{ tabBarLabel: 'Settings!' }}
/>
</SettingsStack.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
正如我们之前提到的,您只能从其屏幕组件之一修改导航器的导航选项。上面的 A
和 B
分别是 HomeStack
和 SettingsStack
中的屏幕组件,而不是标签导航器中的屏幕组件。因此,结果将是 tabBarLabel
属性不会应用于标签导航器。但是,我们可以解决这个问题!
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{ tabBarLabel: 'Home!' }}
/>
<Tab.Screen
name="Settings"
component={SettingsStackScreen}
options={{ tabBarLabel: 'Settings!' }}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
当我们在包含 HomeStack
和 SettingsStack
组件的 Screen
组件上直接设置 options
时,它允许我们控制其父导航器的选项,当它用作屏幕组件时。在这种情况下,我们堆栈组件上的选项配置了渲染堆栈的标签导航器中的标签。
根据子导航器状态设置父屏幕选项
想象一下以下配置
const Tab = createBottomTabNavigator();
function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedScreen} />
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Account" component={AccountScreen} />
</Tab.Navigator>
);
}
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeTabs} />
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
如果我们要使用 `options` 为 `FeedScreen` 设置 `headerTitle`,这将不起作用。这是因为 `App` 堆栈只会查看其直接子级以进行配置:`HomeTabs` 和 `SettingsScreen`。
但是,我们可以使用 `getFocusedRouteNameFromRoute` 辅助函数根据我们标签导航器的 导航状态 来确定 `headerTitle` 选项。让我们先创建一个函数来获取标题
import { getFocusedRouteNameFromRoute } from '@react-navigation/native';
function getHeaderTitle(route) {
// If the focused route is not found, we need to assume it's the initial screen
// This can happen during if there hasn't been any navigation inside the screen
// In our case, it's "Feed" as that's the first screen inside the navigator
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Feed';
switch (routeName) {
case 'Feed':
return 'News feed';
case 'Profile':
return 'My profile';
case 'Account':
return 'My account';
}
}
然后,我们可以将此函数与 `Screen` 上的 `options` 属性一起使用
<Stack.Screen
name="Home"
component={HomeTabs}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
这里发生了什么?使用 `getFocusedRouteNameFromRoute` 辅助函数,我们可以从这个子导航器(在本例中是标签导航器,因为这是我们正在渲染的)获取当前活动的路由名称,并为标题设置一个合适的标题。
这种方法可以在您想要根据子导航器状态为父导航器设置选项时使用。常见的用例是
- 在堆栈标题中显示标签标题:堆栈包含一个标签导航器,您想要在堆栈标题(上面的示例)上设置标题
- 显示没有标签栏的屏幕:标签导航器包含一个堆栈,您想要在特定屏幕上隐藏标签栏(不推荐,请参阅 在特定屏幕中隐藏标签栏)
- 在特定屏幕上锁定抽屉:抽屉在其内部有一个堆栈,您想要在特定屏幕上锁定抽屉
在许多情况下,可以通过重新组织我们的导航器来实现类似的行为。如果适合您的用例,我们通常建议使用此选项。
例如,对于上面的用例,我们可以将堆栈导航器添加到每个标签中,而不是将标签导航器添加到堆栈导航器中。
const FeedStack = createNativeStackNavigator();
function FeedStackScreen() {
return (
<FeedStack.Navigator>
<FeedStack.Screen name="Feed" component={FeedScreen} />
{/* other screens */}
</FeedStack.Navigator>
);
}
const ProfileStack = createNativeStackNavigator();
function ProfileStackScreen() {
return (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Profile" component={ProfileScreen} />
{/* other screens */}
</ProfileStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function HomeTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Feed" component={FeedStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
);
}
const RootStack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen name="Home" component={HomeTabs} />
<RootStack.Screen name="Settings" component={SettingsScreen} />
</RootStack.Navigator>
</NavigationContainer>
);
}
此外,这使您可以在不隐藏标签栏的情况下将新屏幕推送到 feed 和配置文件堆栈,方法是向这些堆栈添加更多路由。
如果您想将屏幕推送到标签栏的顶部(即不显示标签栏),那么您可以将它们添加到 `App` 堆栈中,而不是将它们添加到标签导航器中的屏幕中。