I'm using react-navigation ("^3.0.9") with expo.
This is my logic flow:
TabView(BottomTabNavigator) with StackNavigatior:
HomeStack
--HomeScreen
...
LinksStack
--LinkScreen
...
SettingsStack
-- Aboutscreen
...
Everything works ok, but now I would like to add a drawer navigation (hamburger menu) as follows:
DrawerNavigation View
--HomeScreen(which will show HomeScreen with 3 tabs)
--Screen2 (no tabs)
--Screen3 (no tabs)
Which I tried to do the following:
export const Tab = createBottomTabNavigator({
HomeStack,
LinksStack,
SettingsStack,
}
);
export const Drawer = DrawerNavigator({
HomeScreen: { screen: HomeScreen },
Screen2: { screen: Screen2 },
Screen3: { screen: Screen3 },
});
which they returned an error of undefined is not a function
the original code was to export the default bottom tab navigator only like,
//export default createBottomTabNavigator({
// HomeStack,
// LinksStack,
// SettingsStack,
// }
// );
How do I implement both BottomTabNavigator and DrawerNavigator together?
--Code as below
My App.js which calls AppNavigator
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
}
which AppNavigator calls MainTabNavigator
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import MainTabNavigator from './MainTabNavigator';
export default createAppContainer(createSwitchNavigator({
Main: MainTabNavigator,
}
));
an example of my stackNavigator with Tab
const HomeStack = createStackNavigator({
Home: HomeScreen,
HomeDetail: HomeDetailScreen
});
HomeStack.navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={
Platform.OS === 'ios'
? `ios-home`
: 'md-home'
}
/>
),
};
Update:
I have done a correct working snack example in which anyone can reference from.
You need to add the tabNavigator inside the DrawerNavigator.
const ProfileNavigator = createDrawerNavigator({
Drawer: DashboardTabNav
}, {
initialRouteName: 'Drawer',
contentComponent: ExampleScreen,
drawerWidth: 300
});
// Manifest of possible screens
const PrimaryNav = createStackNavigator({
DashboardScreen: { screen: ProfileNavigator },
LoginScreen: { screen: LoginScreen },
LaunchScreen: { screen: LaunchScreen },
UpdateUserScreen: { screen: UpdateUserScreen }
}, {
// Default config for all screens
headerMode: 'none',
initialRouteName: 'LoginScreen',
navigationOptions: {
headerStyle: styles.header
}
});
export default createAppContainer(PrimaryNav);
Take a look at how to open drawer without navigating to the screen from one of the tabs of tabnavigator?
or
https://readybytes.in/blog/how-to-integrate-tabs-navigation-drawer-navigation-and-stack-navigation-together-in-react-navigation-v2
Also for full example take a look at https://gitlab.com/readybytes/ReactNavigationExampleVersion2
Related
I have made my custom header and i want to remove react-native default header.
I have tried with
Setting option "header: null" in navigationOptions of
createBottomTabNavigator
header:null in HomeScreen.js file
but it's not working. Please help to solve this issue. Here is my navigation code. I am attaching screenshot exactly what i want to remove.
import React from "react";
import { Platform } from "react-native";
import { createStackNavigator } from "react-navigation-stack";
import { createBottomTabNavigator } from "react-navigation-tabs";
import TabBarIcon from "../components/TabBarIcon";
import HomeScreen from "../screens/HomeScreen";
import SavedScreen from "../screens/SavedScreen";
import BookingScreen from "../screens/BookingScreen";
import BeAHostScreen from "../screens/BeAHostScreen";
import ReferEarnScreen from "../screens/ReferEarnScreen";
import BookingInnerScreen from "../screens/BookingInnerScreen";
import { Icon } from "react-native-elements";
const config = Platform.select({
web: { headerMode: "screen" },
default: {}
});
const tabNavigator = createBottomTabNavigator({
Home: {
screen: HomeScreen,
defaultNavigationOptions: {
title: "App Name Here"
},
navigationOptions: {
tabBarLabel: "Home",
tabBarOptions: {
activeTintColor: "#00E8AC"
},
tabBarIcon: ({ focused }) => {
return focused ? (
<Icon name="md-home" type="ionicon" color="#00E8AC" />
) : (
<Icon name="md-home" type="ionicon" color="#ccc" />
);
}
}
},
Saved: {
screen: SavedScreen,
defaultNavigationOptions: {
title: "Saved"
},
navigationOptions: {
tabBarLabel: "Saved",
tabBarOptions: {
activeTintColor: "#00E8AC"
},
tabBarIcon: ({ focused }) => {
return focused ? (
<Icon name="md-heart" type="ionicon" color="#00E8AC" />
) : (
<Icon name="md-heart" type="ionicon" color="#ccc" />
);
}
}
}
});
const MyApp = createStackNavigator(
{
BookingInner: BookingInnerScreen,
Tabs: {
screen: tabNavigator
}
},
{
initialRouteName: "Tabs"
}
);
export default MyApp;
Try this :
const MyApp = createStackNavigator(
{
BookingInner: BookingInnerScreen,
Tabs: {
screen: tabNavigator
}
},
{
initialRouteName: "Tabs",
headerMode: 'none',
}
);
const OneNav = createStackNavigator({
Home: {screen: pages.Home},
Social: {screen: pages.Social},
House: {screen: pages.House},
},{
initialRouteName: 'Home',
});
const TwoNav = createStackNavigator({
Home: {screen: Two}
},{
initialRouteName: 'Home',
});
const TabNav = createBottomTabNavigator({
Home: {screen: OneNav},
Interact: {screen: TwoNav},
},{
initialRouteName: 'Check',
defaultNavigationOptions: {
headerTitle: () => (
<View>
<Logo />
</View>)
}
});
How do you add icons to each tab in the tabnavigator here? Right now only the text is showing. What do I add to TabNav to add icons for Home and Interact?
Below is the code you can try :
// You can import Ionicons from #expo/vector-icons if you use Expo or
// react-native-vector-icons/Ionicons otherwise.
import Ionicons from 'react-native-vector-icons/Ionicons';
import { createAppContainer } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs';
export default createBottomTabNavigator(
{
Home: HomeScreen,
Settings: SettingsScreen,
},
{
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
let IconComponent = Ionicons;
let iconName;
if (routeName === 'Home') {
iconName = `ios-information-circle${focused ? '' : '-outline'}`;
// Sometimes we want to add badges to some icons.
// You can check the implementation below.
IconComponent = HomeIconWithBadge;
} else if (routeName === 'Settings') {
iconName = `ios-options`;
}
// You can return any component that you like here!
return <IconComponent name={iconName} size={25} color={tintColor} />;
},
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
},
}
);
More you can read here from their official docs
I hope this helps....Thanks :)
i am trying to navigate from child tab navigator to a screen of parent stack navigator. But this.props.navigation.navigate can only navigate to child's navigation props
'
//stack.js
const AppNavigator = createStackNavigator(
{
DetailPage: DetailPage, //Screen A
MainScreen: { . //Screen B
screen: MainScreen,
navigationOptions: {
header: null
}
}
{
initialRouteName: "MainScreen"
}
}
//MainScreen.js
<View style={{ flex: 1 }}>
<TabScreen />
</View>
//TabScreen.js
const TabScreen = createMaterialTopTabNavigator({
Home: {
screen: HomeStack
},
// Videos: { screen: Videos },
Videos: {
screen: Videos
},
Shows: {
screen: AllShows
},
Live: {
screen: Live
}
})'
I want to navigate from (TabNavigator)TabScreen's Home to (StackNavigator)AppNavigator's DetailPage. But OnPress does Nothing. However, it can navigate to different screens of TabNavigator(child navigator).
Please help me how can I navigate from child TabNavigator to Parent StackNavigator
Nope. you cant do something like this. You need to make sure there is only one navigator defined. If not, need to make sure the navigation state is connected so that navigators know each other. I would strongly suggest you use a single root navigation
Refer Common Mistakes in react navigation
Explicitly rendering more than one navigator
Most apps should only ever render one navigator inside of a React component, and this is usually somewhere near the root component of your app. This is a little bit counter-intuitive at first but it's important for the architecture of React Navigation.
You need to define everything in a single place (root). Like follows
import React, { Component } from "react";
import { View, Text } from "react-native";
import { createStackNavigator, createAppContainer } from "react-navigation";
import { createMaterialBottomTabNavigator } from "react-navigation-material-bottom-tabs";
import Camera from "./screens/camera";
import Welcome from "./screens/welcome";
import Scanner from "./screens/scanner";
import Cards from ".//screens/cards";
const BottomNavigation = createMaterialBottomTabNavigator(
{
Scan: { screen: Scanner },
Cards: { screen: Cards },
Settings: { screen: Cards }
},
{
initialRouteName: "Scan",
shifting: true
}
);
const AppNavigator = createStackNavigator({
Welcome: {
screen: Welcome,
navigationOptions: { header: null }
},
Camera: {
screen: Camera,
navigationOptions: { header: null }
},
Home: {
screen: BottomNavigation,
navigationOptions: { header: null }
}
});
export default createAppContainer(AppNavigator);
Now i believe you can navigate to different screens as everything is defined on a single navigator.
Could you try this?
//stack.js
const AppNavigator = createStackNavigator(
{
DetailPage: DetailPage, //Screen A
MainScreen: { . //Screen B
screen: MainScreen,
navigationOptions: {
header: null
}
},
TabScreen : {
screen : TabScreen
},
{
initialRouteName: "TabScreen"
}
}
//TabScreen.js
const TabScreen = createMaterialTopTabNavigator({
Home: {
screen: HomeStack
},
// Videos: { screen: Videos },
Videos: {
screen: Videos
},
Shows: {
screen: AllShows
},
Live: {
screen: Live
}
},
{
initialRouteName: "Home"
}
)'
this.props.navigation.dangerouslyGetParent().navigate('routeName', {});
Also, if you are ready to refactor some code you can create a new Screen and create some extra components, you can create a MainTabScreen for each TabNavigation screen, then:-
App.js
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: false
}}
>
<Stack.Screen
name="login"
component={LogIn}
options={{title : 'LOG IN', headerShown: true}}
/>
<Stack.Screen
name="client-tabs-main"
component={ClientTabsMain}
/>
... Other Screens ...
</Stack.Navigator>
</NavigationContainer>
ClientsTabMain.jsx
<Tabs.Navigator
activeColor='white'
barStyle={{backgroundColor: colors.primary}}
>
<Tabs.Screen
name="home-tabs"
component={HomeTab}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home-outline" color={color} size={18} />
),
}}
/>
<Tabs.Screen
name="portfolio-tabs"
component={PortfolioTab}
options={{
tabBarLabel: 'Portfolio',
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="clock" color={color} size={18} />
)
}}
/>
... Other Screens ...
</Tabs.Navigator>
PortfolioTabs.jsx
<PortfolioStack.Navigator
screenOptions={{
headerShown: true
}}
>
<PortfolioStack.Screen
name="portfolio"
component={Portfolio}
options={{
title: 'Portfolio'
}}
/>
<PortfolioStack.Screen
name="transaction"
component={Transaction}
/>
</PortfolioStack.Navigator>
And navigate using :-
this.props.navigation.replace('client-tabs-main',{
screen: 'portfolio-tabs',
params: {
screen: 'portfolio'
}
});
createBottomTabNavigator({
HomeTab: {
screen: HomeStack,
navigationOptions: {
title: "Home",
tabBarOnPress: this.handleTabPress
}
},
MessagingTab: {
screen: MessagingStack,
navigationOptions: {
title: "Messaging",
tabBarOnPress: this.handleTabPress
}
}
});
handleTabPress = ({ navigation }) => {
navigation.popToTop();
navigation.navigate(navigation.state.routeName);
}`
As it says in the title, contentComponent not working. I cant quite figure out why it is not rendering the content in contentcomponent.
It shows the screens 1 2 and logout that I have in the drawer navigator instead of it. Any suggestions is appreciated?
Routes.js
import React from 'react';
import {
createAppContainer,
createSwitchNavigator,
createStackNavigator,
createBottomTabNavigator,
createDrawerNavigator
} from 'react-navigation';
import { SideNavigation } from './SideNavigation';
export const SignedOut = createStackNavigator(
{
Login: {
screen: LoginForm,
navigationOptions: {
header: null
}
},
Register: {
screen: RegisterForm
},
VerifyPhone: {
screen: PhoneVerify,
navigationOptions: {
headerLeft: null
}
}
},
{
initialRouteName: "Login"
}
);
export const Tabs = createBottomTabNavigator({
Main: {
screen: createStackNavigator({
Main: Main
}),
},
Events: {
screen: createStackNavigator({
Events: Events
}),
},
Shop: {
screen: createStackNavigator({
Shop: Shop
}),
},
Profile: {
screen: createStackNavigator({
Profile: Profile
}),
},
});
export const Stack = createStackNavigator(
{
Drawer: {
screen: SideNavigation,
navigationOptions: {
header: null,
},
},
DefaultScreen: {
screen: Main,
}
}
);
export const createRootNavigator = (loggedin) => {
return createAppContainer(createSwitchNavigator(
{
SignedIn: {
screen: Stack
},
SignedOut: {
screen: SignedOut
},
},
{
//initialRouteName: loggedin ? "SignedIn" : "SignedOut"
initialRouteName: "SignedIn"
}
));
};
SideNavigation.js
import { createDrawerNavigator, SafeAreaView } from 'react-navigation';
import { ScrollView } from 'react-native-gesture-handler';
import { Text, Dimensions } from 'react-native';
import { Stack } from "./Routes";
export const SideNavigation = createDrawerNavigator(
{
home: Stack
},
{
contentComponent: (props) => {
return <Drawer {...props} >
<ScrollView>
<SafeAreaView
forceInset={{top: 'always', horizontal: 'never'}}
>
<Text
onPress={() => {
props.navigation.navigate('Screen1');
props.navigation.closeDrawer();
}}
>
Testing Side 1
</Text>
<Text
onPress={() => {
props.navigation.navigate('Screen2');
props.navigation.closeDrawer();
}}
>
Testing side 2
</Text>
</SafeAreaView>
</ScrollView>
</Drawer>
},
}
);
Edit: Code has been updated. i am now getting an error that says 'home'must be a React component.
give your custom component as a separated component file, like this:
import Drawer from "./somewhere";
const Stack = createStackNavigator(
{
DefaultScreen: {
screen: Main,
}
}
);
const DrawerNavigator = createDrawerNavigator({
home: Stack <<<<this is an address of the stack navigator you should created above
}, {
contentComponent: (props) => {
return <Drawer {...props} /> <<<<Drawer
},
drawerPosition: 'right',
drawerType: 'slide',
drawerWidth: width * 0.8
}
)
more logical and cleaner... but don't forget to give drawer to your app container:
return createAppContainer(createSwitchNavigator(
{
SignedIn: {
screen: DrawerNavigato//not stack
},
SignedOut: {
screen: SignedOut
},
},
{
//initialRouteName: loggedin ? "SignedIn" : "SignedOut"
initialRouteName: "SignedIn"
}
));
This is about a React Native + React Navigation issue.
In my project I need a top-level DrawerNavigator so as a shared header among all screens, so that when the drawer opens it stays on top of the header. Best solution I've come up so far was a DrawerNavigator with all of its routes as a StackNavigator with same header config, but that just seems to hacky, ugly and non-performatic, as I think the header would be re-rendering everytime. Has anyone done anything similar? Thanks in advance.
Router.js
export const RegisterStack = StackNavigator(
{
Register: {screen: Register},
},
{
mode: 'modal',
navigationOptions: {
title: 'My title',
}
}
);
export const HistoryStack = StackNavigator(
{
History: {screen: History},
},
{
mode: 'modal',
navigationOptions: {
title: 'My title',
}
}
);
export const FavoritesStack = StackNavigator(
{
Favorites: {screen: Favorites},
},
{
mode: 'modal',
navigationOptions: {
title: 'My title',
}
}
);
export const CardsStack = StackNavigator(
{
Cards: {screen: Cards},
},
{
mode: 'modal',
navigationOptions: {
title: 'My title',
}
}
);
export const AgreementStack = StackNavigator(
{
Agreement: {screen: Agreement},
},
{
mode: 'modal',
navigationOptions: {
title: 'My title',
}
}
);
export const createRootNavigator = () => {
return DrawerNavigator({
Register: {
screen: RegisterStack,
},
History: {
screen: HistoryStack,
},
Favorites: {
screen: FavoritesStack,
},
Cards: {
screen: CardsStack
},
Agreement: {
screen: AgreementStack,
},
})
};
App.js
export default class App extends Component {
render() {
const Layout = createRootNavigator();
return (
<Provider store={store}>
<Layout style={styles.main}/>
</Provider>
);
}
}
Okay I actually got this to work, what you have to do is make a stackNavigator inside the drawer navigator, the drawerNavigator is able to see inside the stackNavigator and navigate between the stacknavigator screens. Using the contentComponent. Once you have done this you can put the shard header inside the stackNavigator.
const PrimaryDrawer = createDrawerNavigator({
PrimaryStack,
}, {
contentComponent: PrimaryDrawerContentComponent,
})
const PrimaryStack = createStackNavigator({
Feed,
Home,
}, {
})
import _ from 'lodash'
export default class PrimaryDrawerContentComponent extends Component {
render() {
const { navigation } = this.props
return (
<FlatList
data={_.keys(navigation.router.childRouters.PrimaryStack.childRouters)}
renderItem={({ item, index }) => (
<DrawerItem
routeName={item}
navigate={navigation.navigate}
icon={icons[index] ? icons[index] : null}
/>
)}
keyExtractor={item => item.key}
/>
)
}
}
const DrawerItem = ({ routeName, navigate, icon }) => {
return (
<TouchableOpacity
style={styles.button}
onPress={() => { navigate(routeName) }}
>
<Image
source={icon}
/>
<Text style={styles.text}>{routeName}</Text>
</TouchableOpacity>
)
}