react-nativeuser-interfacenavigationexpoheader

Profile header displayed in the 'Settings' tab instead of the settings header - React Native/Expo


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:

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

Solution

  • 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.