I'm using react native and Expo.
I have an issue where the profile header is displayed in the 'settings' tab and the settings header is displayed under it.
Image:
The desired outcome is for only the settings header to be displayed in the 'settings' tab and the profile header only to be shown in the profile tab.
My settings code that is showing the settings header -
const Settings = ({ handleLogout }) => (
<SettingsStack.Navigator>
<SettingsStack.Screen
name="SettingsList"
children={(props) => <SettingsList {...props} handleLogout={handleLogout} />}
options={{ title: 'Settings', headerShown: true, headerTitleAlign: 'center', headerTintColor: 'white', headerStyle: { backgroundColor: 'black' } }}
/>
<SettingsStack.Screen
name="SettingTab"
component={SettingTab}
options={({ route }) => ({ title: route.params.settingName, headerShown: true, headerTitleAlign: 'center', headerTintColor: 'white', headerStyle: { backgroundColor: 'black' } })}
/>
</SettingsStack.Navigator>
);
My ProfileStack.Screen: - The header button is a settings icon in the right corner that navigates to 'settings'
<ProfileStack.Navigator>
<ProfileStack.Screen
name="Profile"
options={({ route }) => ({
headerShown: route.name === "Profile", // Only show header when route name is 'Profile'
headerTitle: '', // Set header title to an empty string
headerRight: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
title="Settings"
iconName="settings"
onPress={() => navigation.navigate('Settings')}
/>
</HeaderButtons>
),
})}
>
{props => (
<Profile
{...props}
userProfilePicture={userProfilePicture}
onChangeProfilePicture={setUserProfilePicture}
/>
)}
</ProfileStack.Screen>
<ProfileStack.Screen
name="Settings"
options={{ headerShown: false }}
>
{props => <Settings {...props} handleLogout={handleLogout} />}
</ProfileStack.Screen>
</ProfileStack.Navigator>
My AppNavigator:
const getheaderTitle = (route) => {
const routeName = route.name;
if (routeName === 'Home') {
return 'Home';
} else if (routeName === 'Profile') {
return 'Profile';
}
return ''; // Return an empty string if the route name is not recognized
};
const AppNavigator = ({ userProfilePicture, setUserProfilePicture, handleLogout }) => {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person';
} else {
return null;
}
return <MaterialIcons name={iconName} size={size} color={color} />;
},
headerTitle: getheaderTitle(route),
tabBarActiveTintColor: 'rgb(255, 255, 255)',
tabBarInactiveTintColor: 'rgb(114, 118, 122)',
tabBarStyle: { backgroundColor: 'rgb(0, 0, 0)' },
})}
>
<Tab.Screen
name="Home"
component={Home}
options={{
title: 'Home',
headerStyle: {
backgroundColor: 'rgb(0, 0, 0)', // Customize header background color
},
headerTintColor: 'white', // Customize header text color
}}
initialParams={{ userProfilePicture }}
/>
<Tab.Screen
name="Profile"
children={() => (
<ProfileStackScreen
userProfilePicture={userProfilePicture}
setUserProfilePicture={setUserProfilePicture}
handleLogout={handleLogout}
/>
)}
options={({ route }) => ({
title: 'Profile',
headerTitle: getheaderTitle(route),
headerStyle: {
backgroundColor: 'rgb(0, 0, 0)', // Customize header background color
},
})}
/>
</Tab.Navigator>
);
The issue you're having is due to the structure of your navigation. You have Settings screen as a part of both ProfileStack.Navigator
and SettingsStack.Navigator
. When you're navigating to Settings from Profile, it's trying to render the Settings screen from ProfileStack.Navigator
which includes its own header as well as the one from SettingsStack.Navigator
.
To fix this, you should structure your navigators in a way that avoids nesting of the same screens.
The simple example of this might look something like this:
const RootStack = createStackNavigator();
const RootStackScreen = () => (
<RootStack.Navigator>
<RootStack.Screen name="AppTabs" component={AppNavigator} options=.
{{headerShown: false}}/>
<RootStack.Screen name="Settings" component={Settings} />
</RootStack.Navigator> );
const ProfileStack = createStackNavigator();
const ProfileStackScreen = ({ userProfilePicture,
onChangeProfilePicture, handleLogout }) => (
<ProfileStack.Navigator>
<ProfileStack.Screen
name="Profile"
component={Profile}
initialParams={{ userProfilePicture, onChangeProfilePicture }}
options={({ navigation }) => ({
headerTitle: '',
headerRight: () => (
<HeaderButtons HeaderButtonComponent={CustomHeaderButton}>
<Item
title="Settings"
iconName="settings"
onPress={() => navigation.navigate('Settings')}
/>
</HeaderButtons>
),
})}
/>
</ProfileStack.Navigator> );
Remove the SettingsStack.Screen from ProfileStack.Navigator
and modify the AppNavigator
const AppNavigator = ({ userProfilePicture, setUserProfilePicture,
handleLogout }) => {
return (
<Tab.Navigator
//... your code
>
<Tab.Screen
name="Home"
//... your code
/>
<Tab.Screen
name="Profile"
children={() => (
<ProfileStackScreen
userProfilePicture={userProfilePicture}
onChangeProfilePicture={setUserProfilePicture}
handleLogout={handleLogout}
/>
)}
//... your code
/>
</Tab.Navigator> ); };
Then in your main component where you're using your navigators, use RootStackScreen instead of AppNavigator
export default function App() {
return (
<NavigationContainer>
<RootStackScreen />
</NavigationContainer>
);
}
This is a rough example of what the navigation structure might look like, of course, you will have to adjust this to match your applications needs.