带有嵌套导航器的屏幕选项
在本文档中,我们将解释当存在多个导航器时,屏幕选项是如何工作的。理解这一点非常重要,这样你才能将你的选项放在正确的位置并正确配置你的导航器。如果你把它们放在错误的位置,最好的情况是没有任何事情发生,最坏的情况是会发生令人困惑和意想不到的事情。
你只能从导航器的屏幕组件之一修改该导航器的导航选项。这同样适用于作为屏幕嵌套的导航器。
让我们以一个标签导航器为例,它在每个标签中都包含一个原生堆栈。如果我们在堆栈内部的屏幕上设置选项会发生什么?
- 静态
- 动态
const HomeStackScreen = createNativeStackNavigator({
screens: {
A: {
screen: A,
options: {
tabBarLabel: 'Home',
},
},
},
});
const SettingsStackScreen = createNativeStackNavigator({
screens: {
B: {
screen: B,
options: {
tabBarLabel: 'Settings!',
},
},
},
});
const Tab = createBottomTabNavigator({
screens: {
Home: HomeStackScreen,
Settings: SettingsStackScreen,
},
});
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 属性不会应用于标签导航器。不过,我们可以修复这个问题!
- 静态
- 动态
const Tab = createBottomTabNavigator({
screens: {
Home: {
screen: HomeStackScreen,
options: {
tabBarLabel: 'Home!',
},
},
Settings: {
screen: SettingsStackScreen,
options: {
tabBarLabel: 'Settings!',
},
},
},
});
const Navigation = createStaticNavigation(Tab);
export default function App() {
return <Navigation />;
}
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 组件上时,这允许我们在将其用作屏幕组件时控制其父导航器的选项。在这种情况下,我们堆栈组件上的选项配置了标签导航器中渲染堆栈的标签。
基于子导航器状态设置父屏幕选项
想象以下配置
- 静态
- 动态
const HomeTabs = createBottomTabNavigator({
screens: {
Feed: FeedScreen,
Profile: ProfileScreen,
Account: AccountScreen,
},
});
const RootStack = createNativeStackNavigator({
screens: {
Home: HomeTabs,
Settings: SettingsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
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 设置带有 options 的 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 属性一起使用
- 静态
- 动态
const RootStack = createNativeStackNavigator({
screens: {
Home: {
screen: HomeTabs,
options: ({ route }) => ({
headerTitle: getHeaderTitle(route),
}),
},
Settings: SettingsScreen,
},
});
<Stack.Screen
name="Home"
component={HomeTabs}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
那么这里发生了什么?借助 getFocusedRouteNameFromRoute 辅助函数,我们可以从这个子导航器(在本例中是标签导航器,因为这是我们正在渲染的)获取当前活动的路由名称,并为标题设置适当的标题。
当你想基于子导航器的状态为父导航器设置选项时,可以使用这种方法。常见的用例包括
- 在堆栈标题中显示标签标题:一个堆栈包含一个标签导航器,你想在堆栈标题上设置标题(上面的例子)
- 显示没有标签栏的屏幕:一个标签导航器包含一个堆栈,你想在特定屏幕上隐藏标签栏(不推荐,请参阅 隐藏特定屏幕中的标签栏 代替)
- 在某些屏幕上锁定抽屉:一个抽屉内部有一个堆栈,你想在某些屏幕上锁定抽屉
在许多情况下,通过重新组织我们的导航器可以实现类似的行为。如果它适合你的用例,我们通常推荐这个选项。
例如,对于上述用例,与其在堆栈导航器内部添加标签导航器,不如在每个标签内部添加一个堆栈导航器。
- 静态
- 动态
const FeedStackScreen = createNativeStackNavigator({
screens: {
Feed: FeedScreen,
/* other screens */
},
});
const ProfileStackScreen = createNativeStackNavigator({
screens: {
Profile: ProfileScreen,
/* other screens */
},
});
const HomeTabs = createBottomTabNavigator({
screens: {
Feed: FeedStackScreen,
Profile: ProfileStackScreen,
},
});
const RootStack = createNativeStackNavigator({
screens: {
Home: HomeTabs,
Settings: SettingsScreen,
},
});
const Navigation = createStaticNavigation(RootStack);
export default function App() {
return <Navigation />;
}
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 和 profile 堆栈,而无需隐藏标签栏。
如果你想在标签栏之上推送屏幕(即不显示标签栏的屏幕),那么你可以将它们添加到 App 堆栈,而不是将它们添加到标签导航器内部的屏幕中。