React navigation 5 - header is not shown - react-native

Trying to update my app to react navigation 5 and been confronting some issues.
First of all, the header does not show up. Snips from the code:
[from App.js]
const Tab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} options={{ title:'some title' }}/>
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default App;
and the style of the current screen:
<View style={{flex:1, flexDirection:'column',justifyContent:'space-between'}}>
Here is a screenshot of the app on an Android emulator (and it looks the same on my phone):
As you can see, the header is not shown, the tab navgiation does not right, and so are the buttons (something changed about their background). I did not change anything in the app besides upgrading to react-navigation 5..
Thanks for the help!

Tab navigators do not have header support. You have to wrap your tab navigator inside a stack navigator.
import { createStackNavigator } from "#react-navigation/stack";
// ... other imports
export const App = () => {
return (
<NavigationContainer>
<StackNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const StackNavigator = () => {
return (
<Stack.Navigator>
<Stack.Screen name="Tabs" component={TabNavigator} />
</Stack.Navigator>
);
}
const Tab = createTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator >
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Upload" component={UploadScreen} />
<Tab.Screen name="Find" component={FindScreen} />
</Tab.Navigator>
);
}

Related

Cannot navigate from modal to full screen with no bottom tab in React Native Navigation

I have a bottom tab navigator with 2 icons of HomeScreen and MenuScreen, a separate screen (ProfileScreen), and a modal (ProfileLoginModal). The modal has a button that should redirect ProfileScreen. I put navigation.navigate("ProfileScreen") in the modal, but it's not doing anything, it's not even giving me an error.
But if I change it to one of the screens in the bottom tab nav, for example navigation.navigate("Root", {screen: "HomeScreen}), it works just fine. It just doesn't work with screens outside the bottom tab nav.
NOTE: I do not want a bottom tab on ProfileScreen, so putting it in a nested navigation is not an option.
Can anyone please help me understand what I'm doing wrong?
Navigation structure:
- BottomTabNavigator (Root)
- HomeScreen
- MenuScreen
- ProfileScreen
- ProfileLoginModal
Here is my navigation:
const Stack = createNativeStackNavigator<RootStackParamList>();
<NavigationContainer linking={LinkingConfiguration}>
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabNavigator}
/>
<Stack.Screen
name="ProfileScreen" // ProfileScreen (destination)
component={ProfileScreen}
/>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen
name="ProfileLoginModal" // Go to ProfileScreen from this modal
component={ProfileLoginModal}
/>
</Stack.Group>
</Stack.Navigator>
</NavigationContainer>
const BottomTab = createBottomTabNavigator<RootTabParamList>();
function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="HomeScreen"
screenOptions={...}
>
<BottomTab.Screen
name="HomeScreen"
component={HomeScreen}
options={...}
/>
<BottomTab.Screen
name="Menu"
component={MenuScreen}
options={...}
/>
</BottomTab.Navigator>
);
}
Typings:
export type RootStackParamList = {
Root: NavigatorScreenParams<RootTabParamList> | undefined;
ProfileScreen: undefined;
ProfileLoginModal: undefined;
};
export type RootStackScreenProps<Screen extends keyof RootStackParamList> =
NativeStackScreenProps<RootStackParamList, Screen>;
export type RootTabParamList = {
HomeScreen: undefined;
MenuScreen: undefined;
};
export type RootTabScreenProps<Screen extends keyof RootTabParamList> =
CompositeScreenProps<
BottomTabScreenProps<RootTabParamList, Screen>,
NativeStackScreenProps<RootStackParamList>
>;
I'm using React Native with Expo.
You can do something like this.
const HomeNavigator = () => {
return(
<Stack.Navigator>
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
/>
<Stack.Screen
name="ProfileScreen" // ProfileScreen (destination)
component={ProfileScreen}
/>
<Stack.Group screenOptions={{ presentation: 'modal' }}>
<Stack.Screen
name="ProfileLoginModal" // Go to ProfileScreen from this modal
component={ProfileLoginModal}
/>
</Stack.Group>
</Stack.Navigator>)
}
function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="HomeNavigator"
screenOptions={...}
>
<BottomTab.Screen
name="HomeNavigator"
component={HomeNavigator}
options={...}
/>
<BottomTab.Screen
name="Menu"
component={MenuScreen}
options={...}
/>
</BottomTab.Navigator>
);
}

How do I define multiple types of navigation containers in the same component?

I am new to the world of react native, so I need a hand with structuring my navigation pages. I want my app to have instagram like structure - that is the main page should be createBottomTabNavigator From there I have few different pages, and I can switch between them easily. Here is my code:
export default const MyTabs = ({ currentUser, navigation }) => {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home"
component={HomeScreen}
/>
<Tab.Screen
name="Network"
component={NetworkScreen}
/>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
tabBarIcon: ({ color, size }) => (
<Entypo name="map" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
};
Then in my App.js file I call it like this:
return (
<Provider store={store}>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</Provider>
);
The above code works fine. The problem is when I want to perform more complicated navigation. For instance, in my home page I have list of objects and once, I click on these objects I want to navigate to a new page - with Stack navigator. How do I do so, and where should I define this page? Do I define it somehow inside my Home page, or inside my App.js page? In my mind it should be something like this:
<Stack.Screen
name="Details"
component={DetailScreen}
/>
I tried to put it inside App.js, something like this:
<Provider store={store}>
<NavigationContainer>
<MyTabs />
<Stack.Navigator>
<Stack.Screen
name="Meetup Details"
component={MeetupDetails}
options={{
headerTitle: "Meetup Details",
}}
/>
</Stack.Navigator>
</NavigationContainer>
</Provider>;
But ofc it didn't work.
Also I have another question - in my bottom tab I can navigate to My profile screen and that works fine. But I want to view other profiles as well. So my bottom tab is reserved for my own profile, but how do I define another Profile screen, which can go to other user's profile? Does it need to be with stack navigator? Does it again need to be defined in App.js? Is it ok to define two different navigators (bottom tab and stack) with the same page (Profile)?
Usually, when you have a bottom tab navigator, each tab will be a StackNavigator
export default const MyTabs = ({ currentUser, navigation }) => {
return (
<Tab.Navigator initialRouteName="Home">
<Tab.Screen
name="Home" // here this would be BottomTabRoutes.HOME
component={HomeNavigator}
/>
<Tab.Screen
name="Network" // here this would be BottomTabRoutes.NETWORK
component={NetworkNavigator}
/>
<Tab.Screen
name="Profile" // here this would be BottomTabRoutes.PROFILE
component={ProfileNavigator}
options={{
tabBarIcon: ({ color, size }) => (
<Entypo name="map" color={color} size={26} />
),
}}
/>
</Tab.Navigator>
);
};
And then your MeetupDetails screen will be contained inside the HomeNavigator which is a StackNavigator
export const HomeNavigator = () => {
return (
<Stack.Navigator
screenOptions={{
...
}}>
<Stack.Screen
name={HomeRoutes.HOME}
component={Home}
options={{
headerStyle: styles.header,
title: 'Home'
}}
/>
<Stack.Screen
name={HomeRoutes.MEETUP_DETAILS}
component={MeetupDetails}
options={{
headerStyle: styles.header,
title: 'Meetup Details'
}}
/>
</Stack.Navigator>
}
Also, I would suggest to store your routes names in an enum, in this way it will be easier and their possible route props in a tpye
Something like this:
export enum BottomTabRoutes {
HOME = 'Home',
NETWORK = 'Network',
PROFILE = 'Profile',
}
export type BottomTabRouteProps = {
[BottomTabRoutes.HOME]: {title: string}; // this is another parameter example
[BottomTabRoutes.PROFILE]: undefined;
[BottomTabRoutes.NETWORK]: undefined;
};
And the same for each Stack Navigator:
export enum HomeRoutes = {
HOME = 'Home',
MEETUP_DETAILS = 'Meetup Details',
}
export type HomeRouteProps = {
[HomeRoutes.HOME]: undefined
[HomeRoutes.MEETUP_DETAILS: { someParamName: SomeType } // this might be a parameter you'll send through navigation
}
// Therefore, your MeetupDetails screen will look something like this:
export const MeetupDetails = (props: StackScreenProps<HomeRouteProps, HomeRouteProps.MEETUP_DETAILS>,) => {
const navigationParam = props.route.params.someParamName
return (
<View>
...
</View>
)
}
EDIT
Regarding the Profile Screen, I believe you can have something like this:
export const MainNavigator = () => {
return (
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name={MainNavigatorRoutes.HOME} component={HomeNavigator} />
<Stack.Screen name={MainNavigatorRoutes.USER} component={UserNavigator} />
</Stack.Navigator>
);
};
export const UserNavigator = () => {
return (
<Stack.Navigator
screenOptions={({navigation}) => ({
...)}>
<Stack.Screen
name={UserNavigatorRoutes.SOME_OTHER_SCREEN}
component={UserSettings}
/>
<Stack.Screen
name={UserNavigatorRoutes.USER_PROFILE}
component={UserProfile}
/>
</Stack.Navigator>
);
};
export const HomeNavigator = () => {
return (
<Stack.Navigator screenOptions={{title: ''}} >
<Stack.Screen
name={HomeNavigatorRoutes.HOME_TABS}
component={HomeTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
);
};
export const HomeTabNavigator = () => (
<Stack.Navigator >
<Stack.Screen
name={HomeTabNavigatorRoutes.TAB_NAVIGATOR}
component={MyTabs}
options={{headerShown: false}}
/>
</Stack.Navigator>
Basically, the structure will look like this: MainNavigator = { HomeNavigator, UserNavigator }. In this way, each navigator will be separate and you should be able to navigate to any screen from UserNavigator even if you are in a screen from UserNavigator

Hide header in bottom tab navigator

When I am trying to adding bottom tab navigation then I just got stack tab upper the search components, can you help me to hide or erase it?
Here's my code, and it shows up like this.
const Tab = createBottomTabNavigator();
const Settings = () => (
<SafeArea>
<Text>Settings</Text>
</SafeArea>
);
const Maps = () => (
<SafeArea>
<Text>Maps G</Text>
</SafeArea>
);
import {
useFonts as useOswald,
Oswald_400Regular,
} from "#expo-google-fonts/oswald";
import { useFonts as useLato, Lato_400Regular } from "#expo-google-fonts/lato";
export default function App() {
const [oswaldLoaded] = useOswald({
Oswald_400Regular,
});
const [latoLoaded] = useLato({
Lato_400Regular,
});
if (!oswaldLoaded || !latoLoaded) {
return null;
}
return (
<>
<ThemeProvider theme={theme}>
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Restaurant" component={RestaurantScreen} />
<Tab.Screen name="Maps" component={Maps} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
</NavigationContainer>
</ThemeProvider>
<ExpoStatusBar style={"auto"} />
</>
);
}
Here is my image for the bottom tab navigation ->
show up
You are having a header in the screen as part of BottomTabNavigation.
adding screenOption Like below will solve this
<Tab.Navigator screenOptions={{headerShown: false}} >
<Tab.Screen name="Restaurant" component={RestaurantScreen} />
<Tab.Screen name="Maps" component={Maps} />
<Tab.Screen name="Settings" component={Settings} />
</Tab.Navigator>
You can hide the header with headerShown: false for a single route
<Stack.Navigator
initialRouteName="Settings"
screenOptions={{
headerShown: false, // hide header
}}>
Take a look at my sample
Or for the tab navigator with:
<Tab.Navigator
initialRouteName="Feed"
tabBarOptions={{
headerShown: false,
}}>

Using two Navigators in an app React native

Trying to use bar Navigation and drawer navigator in the same app, and now sure how to make it work.
So currently In the App.js I have a "NavigationContainer", and inside I have a "BarNavigator". whcih works fine, then I wanna add a "DrawerNavigator" inside the "NavigationContainer" then I got an error "Looks like you have nested a 'NavigationContainer' inside another. Normally you need only one container at the root of the app"
then i found this link doing exactly what i wanted ( showing at the end of the page ) and apply what he is doing. still got the same error, then I removed the "BarNavigator" which is the working one, and test out if DrawerNavigator got error, and yes. even got error with the DrawerNavigator only. and here is the code.
App.js
const App = () => {
return (
<NavigationContainer>
//<HomeStackNavigator />
<DrawerNavigator />
</NavigationContainer>
);
};
export default App;
DrawerNavigator.js
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={navigation_bar} />
{/* <Drawer.Screen name="Notifications" component={Ivestment} /> */}
</Drawer.Navigator>
</NavigationContainer>
);
};
export default DrawerNavigator;
Navigator.js
const Stack = createStackNavigator();
const screenOptionStyle = {
headerShown: false,
};
const HomeStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Home" component={BottomTabNavigator} />
<Stack.Screen name="Detail" component={Detail} />
</Stack.Navigator>
);
};
export default HomeStackNavigator;
You are doing it wrong.
You should have only 1 "NavigationContainer" and the navigators should be nested, i.e. one inside of another (parent-child not siblings).
It should look something like this:
App.js
const App = () => {
return (
<NavigationContainer>
<DrawerNavigator />
</NavigationContainer>
);
};
export default App;
DrawerNavigator.js
const Drawer = createDrawerNavigator();
const DrawerNavigator = () => {
return (
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeTabNavigator} />
<Drawer.Screen name="OtherScreen" component={OtherScreen} />
</Drawer.Navigator>
);
};
export default DrawerNavigator;
TabNavigator.js
const Tab = createTabNavigator();
const screenOptionStyle = {
headerShown: false,
};
const HomeTabNavigator = () => {
return (
<Tab.Navigator screenOptions={screenOptionStyle}>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Detail" component={Detail} />
</Tab.Navigator>
);
};
export default HomeTabNavigator;
Might be easier to look at it bottom-top as well.

Using StackScreen options from #react-navigation/native

I need to set a button on screen right side header.
import { useNavigation } from '#react-navigation/native';
const App = () => {
const navigation = useNavigation()
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{headerShown: false}} />
<Stack.Screen name="Next" component={NextScreen} options={{
headerRight: () => <Button title="Done" onPress={() => navigation.navigate('Home')}/>,
headerLeft:null
}} />
<Stack.Screen name="SelectPatient" component={SelectPatientScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}
error message shows that Couldn't find a navigation object. Is your component inside ascreen in a navigator?
I also use navigation in HomeScreen and it works. Error occurs in App.js.
how can i put navigation object onPress.
Thanks.
Just an addition to Guruparan's answer, you can't do const navigation = useNavigation() outside of a navigator