How can a TabNavigator child screen refer to parent StackNavigator? - react-native

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

Related

Adding Header button and title for BottomTabNavigator in Vue Native

I have created a bottom navigator like this:
const IOSTabs = createBottomTabNavigator(
{
Home: {
screen: HomeScreen,
navigationOptions: {
headerStyle: {backgroundColor: 'green'},
title: 'Home',
tabBarLabel: 'Homes',
tabBarIcon: <Icon name="home-outline" size={25}/>,
}
},
Settings: {
screen: SettingsScreen,
navigationOptions: {
tabBarIcon: <Icon name="settings-outline" size={25} />
}
}
}
);
const StackNavigator = createStackNavigator(
{
Home: IOSTabs
}
);
const AppNavigator = createAppContainer(StackNavigator);
export default {
components: { AppNavigator }
}
This works fine, and though the header is showing, the title is empty and cannot add buttons or text to it. I thought that the title should display the header but it doesn't.
What am I missing here?

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 to force drawer to be over header with react navigation

I saw other answers regarding this issue but I can't find solution.
I want to show drawer always above app screen and above header. Instead drawer is always bellow header.
What am I doing wrong here:
const AppNavigation = createStackNavigator(
{
Main: { screen: Main, navigationOptions: {
title: "Main"
} },
Home: { screen: Home, navigationOptions: {
title: "Home"
} }
},
{
initialRouteName: "Main"
}
);
const DrawerNavigation = createDrawerNavigator(
{
Home: Home,
Main: Main
},
{
initialRouteName: "Main"
}
);
App = createStackNavigator({
drawer: {
screen: DrawerNavigation,
},
app: {
screen: AppNavigation
}
}, {
initialRouteName: 'drawer',
navigationOptions: ({navigation}) => ({
headerStyle: {backgroundColor: '#a9a9a9'},
title: 'Welcome!',
headerTintColor: 'white',
headerLeft: <Text onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())}>Menu</Text>
})
});
export default () => (
<View style={{ flex: 1 }}>
<App />
</View>
Our app is similar to this. You can make the drawer be over the header with react-navigation if it is the <Root /> component of your app (the one you import into index.js). For example:
export const Root = DrawerNavigator({
Tabs: {
screen: Tabs,
title: 'Tabs'
},
STACK1: {
screen: STACK1,
title: 'STACK1',
},
STACK2: {
screen: STACK2,
title: 'STACK2',
},
});
That will give you something like this:

react navigation: change tabNavigator style based on redux store

So I'm trying to style the tabNavigator based on the store state:
import React from 'react';
import { connect } from 'react-redux';
import { TabNavigator } from 'react-navigation';
const Tabs = TabNavigator(
// Screens
{
Boards: {
screen: Boards,
navigationOptions: {
tabBarLabel: 'Boards',
tabBarIcon: () => <MaterialCommunityIcon name="bulletin-board" size={25} color="white" />,
},
},
Bookmarks: {
screen: Bookmarks,
navigationOptions: {
tabBarLabel: 'Bookmarks',
tabBarIcon: () => <EntypoIcon name="bookmarks" size={25} color="white" />,
},
},
Settings: {
screen: Settings,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: () => <MaterialCommunityIcon name="settings" size={25} color="white" />,
},
},
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarOptions: {
showIcon: true,
showLabel: false,
renderIndicator: () => null,
style: {
// TODO: Make tabNavigation color change based on current selected theme.
backgroundColor: this.props.theme === 'default' ? 'black' : 'red',
},
},
},
);
const mapStateToProps = state => {
return {
theme: state.configuration.theme,
};
};
export default connect(mapStateToProps)(Tabs);
But when I try to use this.props.theme I get: undefined is not an object (evaluating 'this.props.theme') I guess this happens because tabNavigator doesn't accept props or something like that, so I can't connect tabNavigator to the redux store, so how can I implement what I'm trying to do?
Edit 1
After trying to resolve this using a custom tab bar in the way that was suggested above, this error pops up:
And the code:
TabBar.js
import React from 'react';
import { connect } from 'react-redux';
import { TabBarBottom } from 'react-navigation';
const TabBar = ({ theme }) => (
<TabBarBottom style={{ backgroundColor: theme === 'dark' ? 'black' : 'red' }} />
);
const mapStateToProps = state => ({
theme: state.configuration.theme,
});
export default connect(mapStateToProps)(TabBar);
router.js
import { TabNavigator } from 'react-navigation';
import TabBar from './components/TabBar';
import Boards from './screens/Boards';
import Settings from './screens/Settings';
import Bookmarks from './screens/Bookmarks';
const Tabs = TabNavigator(
// Screens
{
Boards: {
screen: Boards,
navigationOptions: {
tabBarLabel: 'Boards',
tabBarIcon: () => <MaterialCommunityIcon name="bulletin-board" size={25} color="white" />,
},
},
Bookmarks: {
screen: Bookmarks,
navigationOptions: {
tabBarLabel: 'Bookmarks',
tabBarIcon: () => <EntypoIcon name="bookmarks" size={25} color="white" />,
},
},
Settings: {
screen: Settings,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: () => <MaterialCommunityIcon name="settings" size={25} color="white" />,
},
},
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarComponent: TabBar,
},
);
export default Tabs;
You can create your own tab bar, hook it up to the navigator and to redux.
const MyAwesomeTabBar = ({theme}) => (
<View>
...
</View>
)
export default connect(mapStateToProps)(MyAwesomeTabBar);
And then in your navigator definition:
const Tabs = TabNavigator(
// Screens
{
...
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarComponent: MyConnectedAwesomeTabBar
},
);
As for the separation of presentational/functional components - I think that it's not so much that not doing it is bad practice, as much as doing it is good practice. And, you can pretty easily separate it here as well, just have MyAwesomeTabBar be your functional component, which uses a bunch of presentational ones.

How to navigate to a screen from another screen inscribed in a 3 level navigator

I am a newbie to react native. I am using Login and Home screens in a stack navigator in which home screen consist a tab navigator with each tab having 2 or 3 screens; so i added a stack navigator inside that tab navigator (3 Level navigator).
app.js
export const SimpleApp = StackNavigator({
login: { screen: LoginPage },
homepage: { screen : HomePage },
});
home.js
export const MyApp = TabNavigator({
Asset: {
screen: AssetScreen,
navigationOptions: {
tabBarLabel: 'Asset',
tabBarIcon: (
<Image source={require('../logos/tab_assets.png')}
style={[styles.icon, {color: '#ffffff'}]}
/>
)
},
},
Sensors: {
screen: sensorsStack,
navigationOptions: {
tabBarLabel: 'Sensor',
tabBarIcon: (
<Image source={require('../logos/tab_sensor.png')}
style={[styles.icon, {color: '#ffffff'}]}
/>
)
},
},
Settings: {
screen: settingStack,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: (
<Image source={require('../logos/tab_settings.png')}
style={[styles.icon, {color: '#ffffff'}]}
/>
)
},
},
}
With again stack navigator for sensors and settings tab.
export const sensorsStack = StackNavigator({
sensors : { screen: SensorScreen },
sensorDetails : { screen: SensorDetails }
});
export const settingStack = StackNavigator({
settings: { screen: SettingsScreen },
about : { screen: About },
environment : { screen: Environment }
});
I have signout button in settingsScreen, by clicking on it i want to navigate the screen to login which is in app.js stack.
I have tried using nagivation.back() in settingsScreen but it is going back to homepage not login. Is there any way i can achieve this? or any other way to provide login access to my app.