Nesting multiple navigators - react-native

I'm trying to integrate StackNavigator, TabNavigator and DrawerNavigator like this:
class TestApp extends Component {
render() {
return (
<MyApp />
);
}
}
const Tabs = TabNavigator({
Home1: { screen:Home1 },
Home2: { screen:Home2 },
},
{
tabBarPosition: 'bottom',
});
const Drawer = DrawerNavigator({
First:{
screen: DrawerScreen1
},
Second:{
screen: DrawerScreen2
}
},{
initialRouteName:'First',
drawerPosition: 'left'
});
const MyApp = StackNavigator({
Screen1: {screen:Screen1},
Screen2: {screen:Screen2},
Tabs: {
screen: Tabs
},
Drawer:{
screen: Drawer
}
}, {
initialRouteName: "Tabs"
});
AppRegistry.registerComponent('TestApp', () => TestApp);
Calling the drawer:
<Button
onPress={() => this.props.navigation.navigate('DrawerOpen')}
title="Show Drawer"
/>
It does not open the drawer. It opens the DrawerScreen1 as if I'm calling it from StackNavigator. It opens the component with a back button. I want to open the drawer.
DrawerScreen1:
export default class DrawerScreen1 extends Component{
static navigationOptions = {
drawerLabel: 'Home',
drawerIcon: ({ tintColor }) => (
<Image
source={require('./myImage.png')}
style={height:100, width: 100}
/>
),
};
render() {
return (
<Button
onPress={() => this.props.navigation.navigate('DrawerOpen')}
title="DrawerOpen"
/>
);
}
}
DrawerScreen2 is similar. What am I missing? Please help.

Related

How to properly stack nested navigators within an App Container?

I currently have an AppContainer which consists of a bottom tab navigator and currently one more navigation set for Profile settings. I am trying to understand how to properly arrange everything, specifically how to nest navigation in one tab Profile so I can access two other pages QrCode and EditAccount each through two buttons. How do I achieve the below output? I believe I have to nest profile navigation somehow.
MY TABS BELOW
Home
Queue
Profile
:in here i have two other pages i can access QRCode and EditAccounts
App.js
const bottomTabNavigator = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="home" size={25} color={tintColor} />
)
}
},
Queue: {
screen: Queue,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="comments" size={25} color={tintColor} />
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="user" size={25} color={tintColor} />
)
}
}
})
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
//Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
Profile: { screen: Profile },
EditAccount: { screen: EditAccount, navigationOptions }
});
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
After Applying Answer
The solution offers works as needed with the code below:
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
//ADDED
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
The only issue is I am unsure how to port over the header options to the tabs in the in the bottomTabNavigator. I have a custom component in place for the profile page that makes it look like this (black bar with button icon for Profile):
I can then navigate to the EditAccounts by pressing on the user icon. But when I navigate back from EditAccounts back to profile, the page renders with the navigationOptions header like so:
How do I apply this properly so I can lean simply on the navigationOptions header and push a custom name onto it (get rid of my custom component in that case)?
There are 2 cases.
If you want to keep tabs when accessing QR or EditAccount
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: ProfileNavigator,
}
})
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppContainer = createAppContainer(BottomTabNavigator);
If you don't want to keep tabs
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: Profile,
}
})
const AppNavigator = createStackNavigator({
Tabs: BottomTabNavigator,
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
})
const AppContainer = createAppContainer(AppNavigator);
UPDATED:
Add navigationOptions to ProfileNavigator to remove header
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};

How do I conditionally render a header in a Stack Navigator for React Native Navigation?

I would like my initial page to NOT contain a header. However, I would like a header to appear on each subsequent page. Right now my current stackNavigator looks like this:
const AppNavigator = createStackNavigator(
{
HomeScreen: HomePage,
SecondPage: SecondPage,
Account: Account,
About: About,
},
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
navigationOptions: {
headerVisible: false,
},
},
);
export default createAppContainer(AppNavigator);
Here is the basic boilerplate for my second page:
const SecondPage: () => React$Node = () => {
return (
<>
<StatusBar barStyle="dark-content" />
<View style={styles.body}>
<View style={styles.sectionContainer}>
<Text style={styles.sectionTitle}>This is the Secondpage</Text>
</View>
</View>
</>
);
};
export default SecondPage;
You have three ways of doing this using navigationOptions:
1) Directly when you define your navigator:
createStackNavigator({
{
HomeScreen: {
screen : HomePage,
navigationOptions: {header : null}
},
SecondPage: SecondPage,
Account: Account,
About: About,
},
{ ... } //your navigationOptions
})
2) Directly inside the screen as said by Shashank Malviya. Only if you aren't using a functional component.
3) Return defaultNavigationOptions as a function and use navigation state:
createStackNavigator({
{ ... }, //your RouteConfigs
{
initialRouteName: 'HomeScreen',
headerMode: 'none',
defaultNavigationOptions : ({navigation}) => navigation.state.routeName === "HomePage" && {header: null}
},
})
Can be written on every component before render
static navigationOptions = ({ navigation }) => {
const { state } = navigation
return {
headerVisible: true // can be true or false
}
}

Hide bottom tab naivgation

I have a bottom tab bar that locates in app.js. And I have the class where I want to hide the bottom bar. In page home.js I have 2 classes. 1st one is main (is the list of articles), the second one is for button page navigation (in this class I display articles). How I can hide bottom tab navigation in the second page (where articles are displayed). I have tried tabBarVisible: false, but this does not work. Help me, please.
Code:
// app.js
const TabNavigator = createBottomTabNavigator({
Home:{
screen:Home,
navigationOptions:{
tabBarLabel:'Главная',
tabBarIcon:({tintColor})=>(
<Icon name="ios-home" color={tintColor} size={24} />
)
}
},
Courses:{
screen:Courses,
navigationOptions:{
tabBarLabel:'Courses',
tabBarIcon:({tintColor})=>(
<Icon name="ios-school" color={tintColor} size={24} />
)
}
},
Editor:{
screen:Editor,
navigationOptions:{
tabBarLabel:'Editor',
tabBarIcon:({tintColor})=>(
<Icon name="ios-document" color={tintColor} size={24} />
)
}
},
},{
tabBarOptions:{
activeTintColor:'#db0202',
inactiveTintColor:'grey',
style:{
fontSize:3,
height:45,
backgroundColor:'white',
borderTopWidth:0,
elevation: 5
}
}
});
export default createAppContainer(TabNavigator);
// home.js
import React from 'react';
import { Font } from 'expo';
import { Button, View, Text, SafeAreaView, ActivityIndicator, ListView, StyleSheet, Image, Dimensions,
ScrollView } from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation'; // Version can be specified in package.json
import Icon from 'react-native-vector-icons/Ionicons'
import Courses from './Courses'
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home',
};
const { navigate } = this.props.navigation;
return (
<SafeAreaView style={styles.MainContainer}>
<ScrollView
>
<ListView
dataSource={this.state.dataSource}
renderSeparator={this.ListViewItemSeparator}
renderRow={rowData => (
<>
<Text
onPress={() => {
/* 1. Navigate to the Details route with params */
this.props.navigation.navigate("Articles", {
otherParam: rowData.article_title,
});
}}
>
{rowData.article_title}
</Text>
</>
)}
/>
</ScrollView
>
</SafeAreaView>
);
}
}
class ArticleScreen extends React.Component {
static navigationOptions = ({ navigation, navigationOptions }) => {
const { params } = navigation.state;
return {
title: params ? params.otherParam : '',
};
};
render() {
const { params } = this.props.navigation.state;
const article_title = params ? params.otherParam : '';
return (
<Text>{article_title}</Text>
);
}
}
const RootStack = createStackNavigator(
{
Home: {
screen: HomeScreen,
},
Courses: {
screen: Courses,
navigationOptions: {
header: null,
}
},
Articles: {
screen: ArticleScreen,
},
},
{
initialRouteName: 'Home',
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
You have to make StackNavigator as main Navigator and TabBar as a sub navigator:
const TabBar = createBottomTabNavigator(RouteConfigs, TabNavigatorConfig);
const MainNavigator = createStackNavigator(
{
TabBar,
WelcomeScene: { screen:Scenes.WelcomeScene },
HomeScene: { screen: HomeScene }
}
Using this when you go the second screen Tabbar will hide automatically.
Can you try this?
class ArticleScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: params ? params.otherParam : '',
tabBarVisible: false
};
};
...
How about something like this. Create Tab navigator and pass it down to Stack navigator as one of the screen, when you navigate to the Articles, it will hide the tab bar...
const TabNavigator = createBottomTabNavigator({
Home: {
screen: Home,
navigationOptions: {
tabBarLabel: 'Главная',
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-home" color={tintColor} size={24} />
),
},
},
Courses: {
screen: Courses,
navigationOptions: {
tabBarLabel: 'Courses',
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-school" color={tintColor} size={24} />
),
},
},
Editor: {
screen: Editor,
navigationOptions: {
tabBarLabel: 'Editor',
tabBarIcon: ({ tintColor }) => (
<Icon name="ios-document" color={tintColor} size={24} />
),
},
},
}, {
tabBarOptions: {
activeTintColor: '#db0202',
inactiveTintColor: 'grey',
style: {
fontSize: 3,
height: 45,
backgroundColor: 'white',
borderTopWidth: 0,
elevation: 5,
},
},
});
const stackNavigator = createStackNavigator({
Home: {
screen: TabNavigator,
navigationOptions: {
header: null,
},
},
Articles: {
screen: ArticleScreen,
},
// add screens here which you want to hide the tab bar
});
export default createAppContainer(stackNavigator);

How to pass a prop to all tabs of a createBottomTabNavigator

My application is calling a createBottomTabNavigator from a createSwitchNavigator using the following this.props.navigation.navigate('AppMenu', {UserName: 'First Name'}); The createBottomTabNavigator consists of 2 tabs, one is a Stack Navigator and the second is just a React component. When I navigate to the initialRouteName of the Stack Navigator the prop I passed in is available, but if I select the second tab of the Tab Navigator the prop is not available.
Is there a way I can pass a prop to all tabs of a Bottom Tab Navigator when it is rendered?
Here is a diagram of the applications, and sample code that can be run in Snack.
import React from 'react';
import { Text, View, Button } from 'react-native';
import { createBottomTabNavigator, createStackNavigator, createSwitchNavigator } from 'react-navigation';
class LoginScreen extends React.Component {
_navToMain = () => {
console.log('Login Screen: Passing user: First Name');
this.props.navigation.navigate('AppMenu', {UserName: 'First Name'});
}
render () {
return (
<View style={{flexDirection: 'column', paddingTop:20}}>
<Text>Pass UserName prop to createBottomTabNavigator</Text>
<Button title='Login!' onPress={this._navToMain}/>
</View>
)
}
}
class SyncScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'Sync Screen',
};
};
render () {
return (
<View style={{flexDirection: 'column', paddingTop:20}}>
<Text>Sync Screen Navigation Prop UserName (missing!): {JSON.stringify(this.props.navigation.getParam('UserName'))}</Text>
<Button title='Test Button' onPress={() => {}} />
</View>
);
}
}
class AppMenuScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'Stack Navigator',
};
};
_navToOne = () => {
console.log('Going to Stack One')
this.props.navigation.navigate('StackOne', {UserName: this.props.navigation.getParam('UserName')});
}
render () {
return (
<View style={{flexDirection: 'column'}} >
<Text>App Menu Navigation Prop UserName passed from SwitchNavigator: {JSON.stringify(this.props.navigation.getParam('UserName'))}</Text>
<Button title='Screen one' onPress={this._navToOne} />
</View>
)
}
}
class StackOneScreen extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
headerTitle: 'Stack One Screen',
};
};
render () {
return (
<View style={{flexDirection: 'row'}} >
<Text>Stack One Screen Navigation Props: {JSON.stringify(this.props.navigation.getParam('UserName'))}</Text>
</View>
)
}
}
const AppStack = createStackNavigator(
{
AppMenu: AppMenuScreen,
StackOne: StackOneScreen,
},
{
initialRouteName: "AppMenu",
navigationOptions: {
headerTintColor: "#a41034",
headerStyle: {
backgroundColor: "#fff"
}
}
}
);
const MainTabs = createBottomTabNavigator(
{
'Apps': { screen: AppStack },
'Sync': { screen: SyncScreen },
},
{
navigationOptions: ({ navigation }) => ({
title: navigation.state,
tabBarIcon: ({ focused, horizontal, tintColor }) => {
const { routeName } = navigation.state;
}
}),
tabBarOptions: {
activeTintColor: 'red',
inactiveTintColor: 'gray',
tabBackgroundColor: 'black',
labelStyle:{
fontSize: 24,
marginBottom:10,
}
},
animationEnabled: true,
swipeEnabled: false,
}
);
const AppNavigator = createSwitchNavigator(
{
Login: LoginScreen,
Main: MainTabs
},
{
initialRouteName: 'Login',
backBehavior: "initialRoute",
}
);
export default class App extends React.Component {
render() {
return (
<AppNavigator />
);
}
}
Package.json
"react": "16.5.0",
"react-native": "0.57.1",
"react-navigation": "^2.0.0"
you can pass props on navigate using
_navToOne = () => {
console.log("Going to Stack One");
this.props.navigation.navigate({
routeName: "StackOne",
params: { UserName: this.props.navigation.state.params.UserName }
});
};
if you want to access props passed using navigate
this.props.navigation.state.params.PROPNAME
// PROPNAME is the prop you passed and want to get
It's just a temporary solution, at least these code can solve your issue
When I navigate to the initialRouteName of the Stack Navigator the prop I passed in is available, but if I select the second tab of the Tab Navigator the prop is not available
because you just sent the param into AppMenu using this.props.navigation.navigate('AppMenu', {UserName: 'First Name'});
UserName is available on both of tab after i change the code of LoginScreen class :
class LoginScreen extends React.Component {
_navToMain = () => {
console.log('Login Screen: Passing user: First Name');
//-------add this-------
this.props.navigation.navigate('Sync', {UserName: 'First Name'});
//----------------------
this.props.navigation.navigate('AppMenu', {UserName: 'First Name'});
}
render () {
return (
<View style={{flexDirection: 'column', paddingTop:20}}>
<Text>Pass UserName prop to createBottomTabNavigator</Text>
<Button title='Login!' onPress={this._navToMain}/>
</View>
)
}
}

Drawer Navigator with shared header among all children

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