Combine Stack, Drawer and Tab navigator in react-native - react-native

I've currently built, this navigation system but I feel like i've got it a bit wrong as in the order of things. I'm slowly developing issues such as not being able to hide the drawer navigator on certain screens of the tav navigator and etc..
Would someone be able to help me organise the navigation so that it makes a bit more sense?
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const Tab = createMaterialTopTabNavigator();
const SwipeTabNavigator = () => {
return (
<Tab.Navigator
sceneContainerStyle={{
backgroundColor: theme['primaryColor'],
}}
tabBarOptions={{
style: {
display: 'none',
backgroundColor: '#08457e',
},
}}>
<Tab.Screen name="Component1" component={Component1} />
<Tab.Screen name="Component2" component={Component2} />
<Tab.Screen name="Component3" component={Component3} />
</Tab.Navigator>
);
};
const MainStackNavigator = () => {
return (
<Stack.Navigator
screenOptions={{
cardStyle: {backgroundColor: theme['primaryColor']},
header: () => <MenuComponent />,
}}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="SwipeTabNavigator" component={SwipeTabNavigator} />
<Stack.Screen name="List" component={ListScreen} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
);
};
const App = () => {
return (
<Root>
<NavigationContainer>
<Drawer.Navigator
screenOptions={{swipeEnabled: false}}
drawerContent={(props) => <SidebarComponent {...props} />}
initialRouteName="Login">
<Drawer.Screen name="List" component={MainStackNavigator} />
</Drawer.Navigator>
</NavigationContainer>
</Root>
);
};
export default App;
After user logs in the navigation navigates to "SwipeTabNavigator", and I would like the drawer to be available at each screen :/
Any refactor help would be a blessing, thank you!

Related

navigation.navigate() doesn't work on my nested navigations

Here I'am testing with react navigation v6.
When I press "Go To Log-In" button,
it prints "navigation.navigate is not a function.
Here is my Main Navigation
const DrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name="User" component={MainStackNavigator} />
<Drawer.Screen name="Notification" component={BottomTabNavigator}
options={ { headerTitle: props => <LogoTitle {...props} /> }}
/>
</Drawer.Navigator>
);
};
here is my one of my nested Navigation
const MainStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Item" component={Items} />
<Stack.Screen name="Profile" component={Profile} />
<Stack.Screen name="LogIn" component={LogIn} />
<Stack.Screen name="Detail" component={Detail} />
</Stack.Navigator>
);
};
const ContactStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen name="Profile" component={Profile} />
</Stack.Navigator>
);
};
Here is my Log in Button in a component named "Items"
export default function Items(navigation) {
useEffect(() => {
console.log("ITem navi: ", navigation);
}, [])
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Items !</Text>
<Button title="Go to Log-In" onPress={() => navigation.navigate('LogIn')} />
<AccountList />
</View>
);
}
I'am stuck in this error for hours.
Please help me to solve it.
You are accessing it the wrong way
this line
export default function Items(navigation) {
Should be
export default function Items({navigation}) {
Basically you will get all the props as a single parameter which you can destruct and access the navigation prop
In your code complier don't know what is navigation so you need wrap that in porps params like below
export default function Items({navigation}) {

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,
}}>

Always show BottomTabNavigation

How can I show BottomTabNavigation even on stacked screen? I have tried this for a few hours but really don't get it to work as expected.
So the thing I want to happen is, if I navigate to say for example the Title Screen, I still want to show the BottomTabNavigation. Any suggestions?
I can of course create a new navigation, but then it is sliding in from the side.
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
labelStyle: {textTransform: 'uppercase'},
style: {
backgroundColor: '#111111', //Färger på footerbar
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
}}>
<Tab.Screen
name={'Concerts'}
component={ConcertsScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image
source={require('../../assets/icons/concerts.png')}
size={25}
/>
),
}}
/>
<Tab.Screen
name={'Docs'}
component={DocumentiesScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/docs.png')} size={25} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
}}
/>
</Tab.Navigator>
);
};
const Router = () => {
const {token, setToken} = useContext(TokenContext);
const {userFav, addFav, getFav} = useContext(UserContext);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
setTimeout(() => {}, 1000);
}, []);
return (
<NavigationContainer>
{token ? (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTransparent: true,
noBorder: true,
}}
headerMode="float">
<Stack.Screen name={' '} component={HomeTabNavigator} />
<Stack.Screen name={'Concerts'} component={ConcertsScreen} />
<Stack.Screen name={'User Profile'} component={ProfileScreen} />
<Stack.Screen
name={'FavouritesScreen'}
component={FavouritesScreen}
/>
<Stack.Screen name={'Docs'} component={DocumentiesScreen} />
<Stack.Screen name={'AccountScreen'} component={AccountScreen} />
<Stack.Screen name={'Home of'} component={SearchScreen} />
<Stack.Screen name={'Artist'} component={ArtistScreen} />
<Stack.Screen name={'Title'} component={Videos} />
<Stack.Screen name={'PlayVideo'} component={PlayVideo} />
</Stack.Navigator>
) : (
<LoginScreen />
)}
</NavigationContainer>
);
};
You need to nest all your stack screens inside a tab screen.
The BottomTabNavigator disappear because you leave your Tab.Navigator component.
I hope this helps. If you want to navigate between screens that are related to a specific tab button, and have that tab button remain active while moving between these screens, you should set up a StackNavigation within that tab's component. By doing so, the tab button will remain active while navigating within its related screens.
On the other hand, if you want the TabNavigation to be visible throughout the whole application but some screens should not be displayed as tabs, you can add all screens inside the TabNavigation and specify in the options for those screens not to be displayed as tab buttons. That way, while in the screen without a tab button, the tabs will still be visible but none will be active. For example, you can do this for a screen called 'Title':
<Tab.Screen
name={'Title'}
component={Videos}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
tabBarButton: () => null <---- *this causes it to have no button*
}}
/>
I hope this helps!

When using react navigation, to hide the parent component header button from the child component?

Stack navigation is parent and tab navigation is child
I want to hide the button when I press the "settings" tab.
using `react-navigation ver.5
please help me.
child
const Tab = createBottomTabNavigator();
const Tabs = () => {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Observatory" component={Observatory} />
<Tab.Screen name="Search" component={Search} />
<Tab.Screen name="Setting" component={Setting} />
</Tab.Navigator>
);
}
parent
const Stack = createStackNavigator();
export default App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Tabs"
component={Tabs}
options={({ navigation, route }) => ({
headerRight: () => (
<Icon
name="edit"
size={30}
color="#000"
onPress={() => navigation.navigate('Template')}
/>
),
})}
/>
<Stack.Screen name="Template" component={Template} />
</Stack.Navigator>
</NavigationContainer>
);
}