React Native drawer navigation as part of tab navigation - react-native

I'm trying to implement a drawer navigation alongside a tab navigation, but I can't seem to wrap my head around how that would be achieved.
The tab would have 5 items, and should be present on all screens. The 5th tab should open a drawer with more menu items.
Clicking on any of the drawer's menu items should of course show those specific screens, but the tab navigation should be still be present.

In the code below the bottom tab bar (which could be replaced by a top tab navigator) is always present.
The last tab contains the the drawer. It doesn't open by default, but this can be achieved by a this.props.navigation.openDrawer() request.
import React from 'react';
import { View, Text } from 'react-native';
import { createBottomTabNavigator, createDrawerNavigator } from 'react-navigation';
const buildScreen = name => {
class Screen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{name}</Text>
</View>
);
}
}
return Screen;
}
const DrawerScreen = createDrawerNavigator(
{
Child1: buildScreen("DrawerChild 1"),
Child2: buildScreen("DrawerChild 2"),
},
{
// optional drawer options
// cfr. https://reactnavigation.org/docs/en/drawer-based-navigation.html
}
);
export default createBottomTabNavigator({
Tab1: buildScreen("Tab1"),
Tab2: buildScreen("Tab2"),
Tab3: buildScreen("Tab3"),
Tab4: buildScreen("Tab4"),
DrawerScreen,
});

I know this is an old question, but for people still looking for an answer, this is how I implemented mine.
The Drawer Navigation has to be your main navigation. Your tab navigation is nested inside it.
import React from 'react';
import { createDrawerNavigator } from '#react-navigation/drawer';
import AppNavigator from './AppNavigator';
const Drawer = createDrawerNavigator();
function DrawerNavigation() {
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={TabNavigation} />
</Drawer.Navigator>
);
}
export default DrawerNavigation;
In your Tab Navigation, the tab which you choose to show your Drawer Navigation (in your case the fifth tab) would have a listener prop which fires a function on tabPress, toggling the drawer navigation
import React from 'react';
import { Text } from 'react-native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const Home = () => (
<Text> Hello Home</Text>
);
const Menu = () => {
return null;
};
function TabNavigation(props) {
return (
<Tab.Navigator>
<Tab.Screen
name='Home'
component={Home}
/>
<Tab.Screen
name='Menu'
component={Menu}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.toggleDrawer();
},
})}
/>
</Tab.Navigator>
);
}
export default TabNavigation;

You can create a stackNavigator with two screens and a materialTabNavigator inside:
const SomeStack= createStackNavigator({
Start: StartStack,
HomeScreenTab,
}
and then:
HomeScreenTab: {
screen: HomeScreenTabTab
}
with:
export default createMaterialTopTabNavigator(
{
Home: {
screen: HomeStack,
navigationOptions: {
tabBarAccessibilityLabel: 'Tela Inicial do APP',
tabBarLabel: ({ tintColor }) => <LabelTitle tintColor={tintColor}
label="Start" />,
tabBarIcon: ({ tintColor }) => (
<View style={styles.containerIcon}>
<FontAwesome color={tintColor} name="home" size{icons.iconMd}
light />
</View>
),
},
},
...SomeOtherTabs
}
You put the content of your screens on the screen property and so on.

Related

React Navigation : Open drawer when I click on bottom tab navigator

With React Navigation 5, I want to open Drawer when I click on bottom tab navigator (I use material bottom navigator).
I manage to create the bottom tabs buttons and click on them, the home page opens for both tabs (GymIndexScreen or FoodIndexScreen).
When I am on the home pages (GymIndexScreen or FoodIndexScreen), I can open the different Drawers with my fingers (GymDrawerNavigator and FoodDrawerNavigator ) : everything works fine.
Question :
I want the drawers to open / close (toggle) automatically when I click the bottom tabs buttons, without having to open them with my fingers.
App.js :
import { NavigationContainer } from '#react-navigation/native'
const App = () => {
return (
<NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>
)
}
BottomTabNavigator.js :
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
const Tab = createMaterialBottomTabNavigator()
const BottomTabNavigator = (props) => {
return (
<Tab.Navigator>
<Tab.Screen
name="Gym"
component={GymDrawerNavigator}
options={{
tabBarLabel: "Musculation",
)}
/>
<Tab.Screen
name="Food"
component={FoodDrawerNavigator}
options={{
tabBarLabel: "Alimentation",
)}
/>
</Tab.Navigator>
)
}
GymDrawerNavigator.js :
import { createDrawerNavigator } from '#react-navigation/drawer'
const Drawer = createDrawerNavigator()
const GymDrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Gym"
component={GymStackNavigator}
/>
</Drawer.Navigator>
)
}
GymStackNavigator.js :
import { createStackNavigator } from '#react-navigation/stack'
const Stack = createStackNavigator()
const GymStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="GymIndex">
<Stack.Screen
name="GymIndex"
component={GymIndexScreen}
}}
/>
<Stack.Screen
name="GymExerciseIndex"
component={GymExerciseIndexScreen}
}}
/>
... list of screens
If I understood your problem correctly you want to open the drawer automatically when you navigate to the screen?
Add this to the screen components you wish to open the drawer when navigated to.
import {useEffect} from 'react'
...
useEffect(()=>{
navigation.addListener('focus', () => {
// when screen is focused (navigated to)
navigation.openDrawer();
});
},[navigation])``
This answer helped me.
Just use the listeners prop to preventDefault behaviour and then open the drawer.
<Tabs.Screen
name={"More"}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
component={Home}
/>

How do I hide material bottom tab navigator in a nested stack navigator in react native

I'm using material Bottom Tab Navigator, My app is structured such that, some tabs contain a stack navigator.
I want to hide the bottom tabs when a user navigates to another stack in the stack navigator.
I'm using react navigation v5.
I don't want the bottom tabs showing when a user has already navigated to a stack.
I found the answer at this link:
Tab bar can now be hidden #20
What you should do is use the barStyle attribute with the 'none' property, which would look like this:
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
const BottomTab = createMaterialBottomTabNavigator()
const TabsNavigator = () => (
<BottomTab.Navigator
initialRouteName='Home'
barStyle={{
display: 'none'
}}
>
// Screens
</BottomTab.Navigator>
)
Then you can control that property with a variable, something similar to this:
...
barStyle={{
display: isTabVisible ? null : 'none'
}}
...
However, to control which screens are displayed or not, you can use redux or some way to control the state of the variable isTabVisible as shown in the following link:
material-bottom-tabsのTabを非表示する方法〜React navigation〜
Yeap it's in japanese
I made you a basic example of what you're asking. I hope this what you're looking for :
import React from 'react'
import { Button, View, Text, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
import { createStackNavigator } from '#react-navigation/stack'
const Screen1 = ({ navigation }) => (
<View style={styles.component}>
<Button title="Go to NoBottomComp" onPress={() => navigation.navigate('NoBottomComp')} />
</View>
)
const Screen2 = () => <View style={styles.component}><Text>Screen 2 component</Text></View>
const NoBottomComp = () => <View style={styles.component}><Text>Screen without bottom component</Text></View>
const Footer = createMaterialBottomTabNavigator()
const FooterNav = () => (
<Footer.Navigator>
<Footer.Screen name="Screen1" component={Screen1} />
<Footer.Screen name="Screen2" component={Screen2} />
</Footer.Navigator>
)
const Main = createStackNavigator()
export default props => (
<NavigationContainer>
<Main.Navigator>
<Main.Screen name="BottomNav" component={FooterNav} />
<Main.Screen name="NoBottomComp" component={NoBottomComp} />
{/* As many screens as you want to be without bottom tabs */}
</Main.Navigator>
</NavigationContainer>
)
const styles = StyleSheet.create({
component: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
})

How to add React-Navigation Drawer menu to my screens?

I have a home screen built with React-Navigation 3.x. I have a header, some navigation icons, and a bottom tab menu. It is working well, but now I want to add a drawer menu to the screen and add an icon at the top right corner to toggle the drawer.
Here's a simplified version of my home screen (App.js):
import { createStackNavigator, createAppContainer, createBottomTabNavigator } from 'react-navigation';
import Activity1 from './Activity/Activity1';
import Activity2 from './Activity/Activity2';
import Activity3 from './Activity/Activity3';
import Calendar from './Screens/Calendar';
import Graph from './Screens/Graph';
import DrawerMenu from './components/DrawerMenu';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
headerRight: (<Button onPress={() => this.props.navigation.toggleDrawer()} title='Menu' />)
};
render() {
return (
<View style={styles.container}>
<View style={styles.iconContainer}>
<Icon name='icon1' onPress={this.navToActivity1} />
<Icon name='icon2' onPress={this.navToActivity2} />
<Icon name='icon3' onPress={this.navToActivity3} />
</View>
<View>
<DrawerMenu />
</View>
</View>
);
}
}
//create my main navigation stacks here
const Home = createStackNavigator({
HomeScreen,
Activity1,
Activity2,
Activity3,
});
//The following two are for the bottom tab bar only
const Calendar = createStackNavigator({ Calendar });
const Graph = createStackNavigator({ Graph });
const BottomTabNav = createBottomTabNavigator({
Home, Calendar, Graph
});
export default createAppContainer(TabNavigator);
And here's the code for DrawerMenu.js
import { Dimensions } from 'react-native';
import { createDrawerNavigator, createAppContainer } from 'react-navigation';
import Settings from './Settings';
import Profile from './Profile';
const SCREENWIDTH = Dimensions.get('window').width;
const DrawerConfig = {
drawerWidth: SCREENWIDTH * 0.5,
drawerPosition: 'right',
};
const DrawerMenu = createDrawerNavigator({
Settings: { screen: Settings },
Profile: { screen: Profile },
},
DrawerConfig
);
export default createAppContainer(DrawerMenu);
I couldn't get the drawer to work. When I click on the "Menu" button at the top right corner in the Home Screen to invoke toggleDrawer(), I got an "undefined is not an object (evaluating 'ae.props.navigation')" error.
The drawer cannot be activated using gesture either, so I think I am not adding it correctly. What did I do wrong here? Thanks!!
install the package the react-native-drawer
https://www.npmjs.com/package/react-native-drawer
<Drawer
type="overlay"
ref={ref => (this._drawer = ref)}
content={<Sidebar />}
tapToClose={true}
openDrawerOffset={0.2} // 20% gap on the right side of drawer
panCloseMask={0.2}
closedDrawerOffset={-3}
styles={drawerStyles}
tweenHandler={ratio => ({ main: { opacity: (2 - ratio) / 2 } })}
>
cds

Is a StackNavigator inside TabNavigator possible?

I have created a custom tab navigator using createMaterialTopTabNavigator but inside one of the tabs I would like to create a StackNavigator. Is this possible?
I have found lots of examples of Tabs nested inside a Stack or Drawer but not an example of a Stack inside a Tab!
In essence I want to have a few buttons on one of the Tab Screens that navigate to some other screens but I don't want to navigate out of the Tab. (the initial Tab is always visible/selected)- just that you can click on a button and go to another screen and then back.
TabNavigator:
- Settings Screen (Tab 1)
-About Us (Button when clicked opens up the About Us Screen)
-Account Settings (Button when clicked opens up the About Us Screen)
-Contact Us (Button when clicked opens up the About Us Screen)
- Search Screen (Tab 2)
- Profile Screen (Tab 3)
Any suggestions on if they is possible would be greatly appreciated! :)
Yes you can do that
You can nest StackNavigator inside TabNavigator by doing something similar to this -
import { TabNavigator, StackNavigator } from 'react-navigation'
export const TabNavigator = TabNavigator({
SettingsScreenStack: { screen: SettingsScreenStack },
SearchScreen: { screen: SearchScreen },
ProfileScreen: { screen: ProfileScreen },
}, {
order: ['SettingsScreenStack', 'SearchScreen', 'ProfileScreen'],
initialRouteName: 'SettingsScreenStack',
});
export const SettingsScreenStack = StackNavigator({
AboutUsScreen: { screen: AboutUsScreen },
AccountSettingsScreen: { screen: AccountSettingsScreen },
ContactUsScreen: { screen: ContactUsScreen },
}, {
initialRoute: 'AboutUsScreen',
})
...
Hope it helped.
Yes you can nest StackNavigator inside TabNavigator
Visit:- Stack Navigator for each Tab
import * as React from 'react';
import { Button, Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
function DetailsScreen() {
return (
/* content */
);
}
function HomeScreen({ navigation }) {
return (
/* content */
);
}
function SettingsScreen({ navigation }) {
return (
/* content */
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={HomeScreen} />
<HomeStack.Screen name="Details" component={DetailsScreen} />
</HomeStack.Navigator>
);
}
const SettingsStack = createStackNavigator();
function SettingsStackScreen() {
return (
<SettingsStack.Navigator>
<SettingsStack.Screen name="Settings" component={SettingsScreen} />
<SettingsStack.Screen name="Details" component={DetailsScreen} />
</SettingsStack.Navigator>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}

How to implement both DrawerNavigator and StackNavigator

I am developing an app using react-native-navigation and I want to have a StackNavigator and a DrawerNavigator in the project. So, I have implemented them in the app.js but the apps crashes giving this error "The development server returned response error with code: 500".I have implemented them separetly and it works but I couldn't implement them together.Any suggestion?
this is my code for app.js
import React, {
Component
} from 'react';
import {
StyleSheet,
Text,
View
} from 'react-native';
import {
createStackNavigator,
DrawerNavigator,
DrawerItems
} from "react-navigation";
import {
Container,
Header,
Content,
Thumbnail,
Button,
Body
} from 'native-base';
import Profile from './components/Profile.js';
import Main from './components/Main.js';
import Login from './components/Login.js';
import Product from './components/Product.js';
export default class App extends Component {
render() {
return (
<Navapp />
);
}
}
const styles = StyleSheet.create({
// styles here
});
export const Drawer = DrawerNavigator({
Main: {
screen: Main
},
Profile: {
screen: Profile
},
}, {
initialRouteName: 'Main',
contentComponent: CustomDrawer,
drawerPosition: 'Left',
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
});
export const CustomDrawer = (props) => (
<Container>
<Header style = {
styles.headerStyle
}>
<Body style = {
styles.bodyStyle
}>
<Thumbnail style = {
{
height: 100,
width: 100
}
}
source = {
require('../image/logo.png')
}/>
</Body>
</Header>
<Content>
<DrawerItems { ...props} />
</Content >
</Container>
)
export const Navapp = createStackNavigator({
Login: {
screen: Login
},
Drawer: {
screen: Drawer
},
Product: {
screen: Product
},
});
I had a very similar setup for my app, this is how I went about handling it. First I created a stack navigator with the routes that I wanted logged in users to see, and I place that navigator inside a drawer navigator (you can put more than one in there if you want). Finally I created my top-level navigator, whose initial route points to the login page; upon logging in I navigate the user to the second route, which points to my drawer navigator.
In practice it looks like this:
// Main Screens for Drawer Navigator
export const MainStack = StackNavigator({
Dashboard: {
screen: Dashboard,
navigationOptions: {
title: 'Dashboard',
gesturesEnabled: false,
headerLeft: null
}
},
Notifications: {
screen: Notifications,
navigationOptions: {
title: 'Notifications'
}
}
}, { headerMode: 'screen' } );
// Drawer Navigator
export const Drawer = DrawerNavigator({
MainStack: {
screen: MainStack
}
});
// Main App Navigation
export const AppStack = StackNavigator({
Login: {
screen: Login,
navigationOptions: {
header: null,
gesturesEnabled: false
}
},
Drawer: {
screen: Drawer,
navigationOptions: {
header: null,
gesturesEnabled: false
}
}
}, { headerMode: 'none' } );
// In Your App.js
<AppStack />
Note that in the last stack navigator, the drawer screen has header set to null; this is because with nested stack navigators you can sometimes run into an issue where you'll have duplicate headers. This will hide the top-level navigator's header and let you show / customize the headers for your nested navigators.
Probably the way we can implement both Stack and Drawer Navigation has been made much simpler. Here you guys can refer to my code.
import * as React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { createDrawerNavigator } from "#react-navigation/drawer";
import MenuComponent from "../MenuComponent";
import DishDetailComponent from "../DishDetailComponent";
import HomeComponent from "../HomeComponent";
/**
* #author BadalSherpa
* #function HomeNavigation
**/
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const HomeNavigation = (props) => {
return (
<Stack.Navigator initialRouteName='Home'>
<Stack.Screen name='Home' component={HomeComponent} />
<Stack.Screen name='Menu' component={MenuComponent} />
<Stack.Screen name='Dish-Detail' component={DishDetailComponent} />
</Stack.Navigator>
);
};
const MenuNavigation = (props) => {
return (
<Stack.Navigator initialRouteName='Home'>
<Stack.Screen name='Menu' component={MenuComponent} />
</Stack.Navigator>
);
};
const DrawerNavigation = () => {
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomeNavigation} /> //Here is where we are combining Stack Navigator to Drawer Navigator
<Drawer.Screen name='Menu' component={MenuNavigation} />
</Drawer.Navigator>
);
};
export default DrawerNavigation;
Then you can simple return this inside NavigationContainer which will have both Stack and Drawer Navigator working together.
<NavigationContainer>
<HomeNavigation />
</NavigationContainer>
This is how I use them
const HomeStackNavigator = StackNavigator(
{
Home: {
screen: HomeScreen,
},
Chat: {
screen: ChatScreen,
},
},
{
initialRouteName: 'Home',
headerMode: 'screen',
},
);
const MainDrawerNavigator = DrawerNavigator(
{
Home: {
screen: HomeStackNavigator,
},
},
{
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
contentComponent: SlideMenu,
navigationOptions: {
drawerLockMode: 'locked-closed',
},
},
);
same issue with me, Then i just change my emulator and update the version and it worked 100%.
This is a slightly different take where the drawer navigator controls a stack navigator. Each selection in the drawer will push a child stack element so there is a one-to-one match between the drawer and stack. This is more typical behavior in my experience. All but the home have a title with back navigation indicator.
import React from 'react';
import {SafeAreaView, StyleSheet} from 'react-native';
import {
CompositeNavigationProp,
NavigationContainer,
} from '#react-navigation/native';
import {
createStackNavigator,
StackNavigationProp,
} from '#react-navigation/stack';
import HomeScreen from './HomeScreen';
import SettingsScreen from './SettingsScreen';
import {
createDrawerNavigator,
DrawerContentComponentProps,
DrawerContentOptions,
DrawerContentScrollView,
DrawerItem,
DrawerNavigationProp,
} from '#react-navigation/drawer';
type NoProps = Record<string, object>;
export type ScreenNavigationProps = CompositeNavigationProp<
StackNavigationProp<NoProps>,
DrawerNavigationProp<NoProps>
>;
export type DrawerProps = DrawerContentComponentProps<DrawerContentOptions>;
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator(); // https://reactnavigation.org/docs/stack-navigator/
/**
* Render the content of the drawer. When an item is selected
* it closes the drawer and pushes an element on the stack nav.
* #param props
*/
function CustomDrawerContent(props: DrawerProps) {
const navigation = (props.navigation as unknown) as ScreenNavigationProps;
const openScreen = (name: string) => () => {
navigation.navigate('Home', {screen: name});
navigation.closeDrawer();
};
return (
<DrawerContentScrollView {...props}>
{/* <DrawerItemList {...props} /> */}
<DrawerItem label="Home" onPress={openScreen('Home')} />
<DrawerItem label="Settings" onPress={openScreen('Settings')} />
</DrawerContentScrollView>
);
}
/**
* Render the Home Stack Navigator.
*/
function HomeStack() {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
name="Home"
component={HomeScreen}
options={{headerShown: false}}
/>
<Stack.Screen name="Settings" component={SettingsScreen} />
</Stack.Navigator>
);
}
/**
* Render the Home Drawer with a custom drawerContent.
*/
function HomeDrawer() {
return (
<Drawer.Navigator
initialRouteName="Home"
drawerContent={(props) => <CustomDrawerContent {...props} />}>
<Drawer.Screen name="Home" component={HomeStack} />
</Drawer.Navigator>
);
}
const App = () => {
return (
<SafeAreaView style={styles.app}>
<NavigationContainer>
<HomeDrawer />
</NavigationContainer>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
app: {flex: 1},
});
export default App;
The HomeScreen can provide a menu to open the drawer:
const navigation = useNavigation<ScreenNavigationProps>();
const openDrawer = () => {
navigation.openDrawer();
};