Skip to main content
Version: 6.x

带有嵌套导航器的屏幕选项

在本文档中,我们将解释当存在多个导航器时 屏幕选项 如何工作。了解这一点很重要,这样你才能将 options 放在正确的位置并正确配置导航器。如果你把它们放在错误的地方,最好的情况是什么也不会发生,最坏的情况是会发生一些令人困惑和意想不到的事情。

¥In this document we'll explain how screen options work when there are multiple navigators. It's important to understand this so that you put your options in the correct place and can properly configure your navigators. If you put them in the wrong place, at best nothing will happen and at worst something confusing and unexpected will happen.

你只能从导航器的屏幕组件之一修改导航器的导航选项。这同样适用于嵌套为屏幕的导航器。

¥You can only modify navigation options for a navigator from one of its screen components. This applies equally to navigators that are nested as screens.

我们以一个选项卡导航器为例,其中每个选项卡中都包含一个原生堆栈。如果我们将 options 设置在堆栈内部的屏幕上会发生什么?

¥Let's take for example a tab navigator that contains a native stack in each tab. What happens if we set the options on a screen inside of the stack?

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>
);
}

正如我们之前提到的,你只能从导航器的屏幕组件之一修改导航器的导航选项。上面的 AB 分别是 HomeStackSettingsStack 中的屏幕组件,不在选项卡导航器中。因此,结果将是 tabBarLabel 属性不会应用于选项卡导航器。不过我们可以解决这个问题!

¥As we mentioned earlier, you can only modify navigation options for a navigator from one of its screen components. A and B above are screen components in HomeStack and SettingsStack respectively, not in the tab navigator. So the result will be that the tabBarLabel property is not applied to the tab navigator. We can fix this though!

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>
);
}

当我们直接在包含 HomeStackSettingsStack 组件的 Screen 组件上设置 options 时,它允许我们在将其用作屏幕组件时控制其父导航器的选项。在这种情况下,堆栈组件上的选项配置呈现堆栈的选项卡导航器中的标签。

¥When we set the options directly on Screen components containing the HomeStack and SettingsStack component, it allows us to control the options for its parent navigator when its used as a screen component. In this case, the options on our stack components configure the label in the tab navigator that renders the stacks.

根据子导航器的状态设置父屏幕选项

¥Setting parent screen options based on child navigator's state

想象一下以下配置:

¥Imagine the following configuration:

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>
);
}

如果我们为 FeedScreen 设置 headerTitleoptions,这是行不通的。这是因为 App 堆栈只会查看其直接子堆栈进行配置:HomeTabsSettingsScreen

¥If we were to set the headerTitle with options for the FeedScreen, this would not work. This is because App stack will only look at its immediate children for configuration: HomeTabs and SettingsScreen.

但是我们可以使用 getFocusedRouteNameFromRoute 辅助程序根据选项卡导航器的 导航状态 确定 headerTitle 选项。让我们先创建一个函数来获取标题:

¥But we can determine the headerTitle option based on the navigation state of our tab navigator using the getFocusedRouteNameFromRoute helper. Let's create a function to get the title first:

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 属性一起使用:

¥Then we can use this function with the options prop on Screen:

<Stack.Screen
name="Home"
component={HomeTabs}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>

那么这里发生了什么?使用 getFocusedRouteNameFromRoute 辅助程序,我们可以从这个子导航器获取当前活动的路由名称(在本例中它是选项卡导航器,因为这就是我们正在渲染的内容)并为标题设置适当的标题。

¥So what's happening here? With the getFocusedRouteNameFromRoute helper, we can get the currently active route name from this child navigator (in this case it's the tab navigator since that's what we're rendering) and setting an appropriate title for the header.

当你想要根据子导航器的状态为父导航器设置选项时,可以随时使用此方法。常见用例有:

¥This approach can be used anytime you want to set options for a parent navigator based on a child navigator's state. Common use cases are:

  1. 在堆栈标题中显示选项卡标题:堆栈包含一个选项卡导航器,并且你想要在堆栈标题上设置标题(上面的示例)

    ¥Show tab title in stack header: a stack contains a tab navigator and you want to set the title on the stack header (above example)

  2. 显示没有标签栏的屏幕:选项卡导航器包含一个堆栈,并且你想要隐藏特定屏幕上的选项卡栏(不推荐,请参阅 在特定屏幕中隐藏标签栏

    ¥Show screens without tab bar: a tab navigator contains a stack and you want to hide the tab bar on specific screens (not recommended, see Hiding tab bar in specific screens instead)

  3. 在某些屏幕上锁定抽屉:抽屉里有一堆东西,你想在某些屏幕上锁定抽屉

    ¥Lock drawer on certain screens: a drawer has a stack inside of it and you want to lock the drawer on certain screens

在许多情况下,可以通过重组我们的导航器来实现类似的行为。如果它适合你的用例,我们通常会推荐此选项。

¥In many cases, similar behavior can be achieved by reorganizing our navigators. We usually recommend this option if it fits your use case.

例如,对于上面的用例,我们可以在每个选项卡内添加一个堆栈导航器,而不是在堆栈导航器内添加选项卡导航器。

¥For example, for the above use case, instead of adding a tab navigator inside a stack navigator, we can add a stack navigator inside each of the tabs.

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>
);
}

此外,这使你可以将新屏幕推送到源和配置文件堆栈,而无需通过向这些堆栈添加更多路由来隐藏选项卡栏。

¥Additionally, this lets you push new screens to the feed and profile stacks without hiding the tab bar by adding more routes to those stacks.

如果你想将屏幕推到选项卡栏的顶部(即不显示选项卡栏),那么你可以将它们添加到 App 堆栈,而不是将它们添加到选项卡导航器内的屏幕中。

¥If you want to push screens on top of the tab bar (i.e. that don't show the tab bar), then you can add them to the App stack instead of adding them into the screens inside the tab navigator.