react-navigation goBack between stack navigators - react-native

In my React native app I am using "react-navigation": "^3.11.0".
I have top level bottomTabNavigator
const TabNavigator = createBottomTabNavigator({
Main: {
screen: balanceStack,
navigationOptions: {
tabBarLabel: I18n.t("balanceTabLabel"),
tabBarIcon: ({ tintColor}: {tintColor: string}) => (
<Icon name="home" style={{color: tintColor}} />
)
}
},
ServicesStack: {
screen: servicesStack,
navigationOptions: {
tabBarLabel: I18n.t("servicesTabLabel"),
tabBarIcon: ({ tintColor}: {tintColor: string}) => (
<Icon name="list-box" style={{color: tintColor}} />
)
}
},
}, {
tabBarOptions: {
activeTintColor: WHITE_COLOR,
inactiveTintColor: WHITE_COLOR,
style: {
backgroundColor: PRIMARY_COLOR,
}
},
backBehavior: 'history',
swipeEnabled: true,
});
And stack navigators for each tab:
const balanceStack = createStackNavigator({
Balance: {
screen: MainScreen,
},
FullBalance: {
screen: FullBalanceScreen,
},
Payment: {
screen: PaymentScreen,
},
ServiceView: {
screen: ViewServiceScreen,
},
}, {
initialRouteName: 'Balance',
headerMode: 'none',
});
const servicesStack = createStackNavigator({
AllServices: {
screen: AllServicesScreen,
},
ServiceView: {
screen: ViewServiceScreen,
},
ServiceOptionAction: {
screen: ServiceOptionsActionScreen,
}
}, {
initialRouteName: 'AllServices',
headerMode: 'none',
});
I want that my navigation for all tabs will be common, not divided per stack.
For example
when I navigate
Balance->FullBalanceScreen->AllServices(by clicking Services tab)->ServiceView
If I click back button (call goBack()) one time I will back to AllServices. But if I click back second time, I don't navigate to FullBalanceScreen, because it's in another stack. How can I do this?

Finally, I didn't find solution with TabNavigator and replace it with top level StackNavigator.
Also I use custom bottom Tabs component and set onPress for tab button to
onPress () {
        this.props.navigation.navigate (this.props.navigateRouteName)
    }
Where navigateRouteName is routeName to target stack route.

If you define a custom header (either for the top level nav, or duplicated for each stack) then navigation object will have the global history (not just for each stack) so clicking back will jump between tabs
<A.Navigation screenOptions={{header: ({nav}) => <CustomHeader nav/>}>
and in you header you can conditionally show a back button and add handler
if (nav.canGoBack()) // show button
... nav.goBack() // onPress

Related

Is it better to nest multiple navigators in a stack or switch navigator?

I have both a Material Top Tab Navigator and a Drawer Navigator and both successfully display when either are nested inside a Stack/Switch Navigator. However I can't tell the difference. Is there any difference to using either one and if so which is better or is it down to preference.
The following is what I'm using at the moment with a Switch Navigator:
const TabNavigator = createMaterialTopTabNavigator(
{
Calculator: {
screen: CalculatorScreen,
navigationOptions: {
tabBarIcon: ({}) => (
<Icon name='ios-calculator' size={28} color='#FFF'/>
)
}
},
Camera: {
screen: CameraScreen,
navigationOptions: {
tabBarIcon: ({}) => (
<Icon name='ios-camera' size={28} color='#FFF'/>
)
}
},
Solution: {
screen: SolutionScreen,
navigationOptions: {
tabBarIcon: ({}) => (
<Icon name='ios-paper' size={28} color='#FFF'/>
)
}
}
},
{
animationEnabled: false,
initialRouteName: 'Camera',
tabBarPosition: 'bottom',
tabBarOptions: {
showIcon: true,
showLabel: false,
indicatorStyle: {
opacity: 0
},
style: {
backgroundColor: 'transparent',
position: 'absolute',
left: 0,
bottom: 0,
right: 0
}
}
}
)
const DrawerNavigator = createDrawerNavigator(
{
Contact: {
screen: TabNavigator
}
},
{
drawerBackgroundColor: 'rgba(255,255,255,.9)',
contentOptions: {
activeTintColor: '#fff',
activeBackgroundColor: '#6b52ae',
},
}
);
const SwitchNavigator = createSwitchNavigator(
{
Drawer: {
screen: DrawerNavigator
},
Screen: {
screen: TabNavigator
}
}
)
export default AppContainer = createAppContainer(SwitchNavigator)
In this either case I get desirable results that are almost identical. So my only questions are which is better and what are the differences?
The difference between a switchnavigator and a stacknavigator is whether you stack the stack or not. StackNavigator builds up the stack by default each time you move the screen. This means that you'll see the screen on top of the screen You can therefore execute the 'goBack()' button. However, the switchnavigator does not stack. The switchnavigator resets the path as it moves the screen.
SwitchNavigator: The purpose of SwitchNavigator is to only ever show one screen at a time. By default, it does not handle back actions
and it resets routes to their default state when you switch away
StackNavigator: Provides a way for your app to transition between screens where each new screen is placed on top of a stack.

Uber-like Drawer navigation in React Native app

I'm trying to figure out how to implement navigation in React Native app using react-navigation to make it similar to Uber app.
I'd like to use Drawer which contains a menu items like Settings, Profile etc. Click on each item should open a modal with navigation Stack with arrow left or cross icon button on the top (header) to close stack and back to main screen. When modal with navigation stack is opened I'd like to disable drawer.
Is this possible to achieve with react-navigation?
Yes react navigation drawer accepts arbitrary react components as content. You can see an example by running https://expo.io/#react-navigation/NavigationPlayground on your phone.
Some example code from https://github.com/react-navigation/react-navigation/blob/master/examples/NavigationPlayground/src/Drawer.tsx
const InboxStack = createStackNavigator(
{
Email: { screen: EmailScreen },
Inbox: { screen: InboxScreen },
},
{
navigationOptions: {
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="move-to-inbox"
size={24}
style={{ color: tintColor } as StyleProp<TextStyle>}
/>
),
drawerLabel: 'Inbox',
},
}
);
const DraftsStack = createStackNavigator(
{
Drafts: { screen: DraftsScreen },
Email: { screen: EmailScreen },
},
{
navigationOptions: {
drawerIcon: ({ tintColor }) => (
<MaterialIcons
name="drafts"
size={24}
style={{ color: tintColor } as StyleProp<TextStyle>}
/>
),
drawerLabel: 'Drafts',
},
}
);
const DrawerExample = createDrawerNavigator(
{
Drafts: {
path: '/sent',
screen: DraftsStack,
},
Inbox: {
path: '/',
screen: InboxStack,
},
},
{
contentOptions: {
activeTintColor: '#e91e63',
},
initialRouteName: 'Drafts',
}
);

React native - onpress doesn't work inside drawer navigation header

I'm trying to add a custom menu icon button inside the navigation header in my react native app. I successfully did that, but the press event doesn't fire as long as the icon is in the header. For example, if I have the icon here:
https://www.dropbox.com/s/xyah9ei43wgt1ut/menu_regular.png?dl=0
The press event doesn't work, but if I have it here (moved it lower):
https://www.dropbox.com/s/54utpr1efb3o0lm/menu_moved.png?dl=0
The event fires ok.
Here's my current setup navigator:
const MainNavigator = createStackNavigator(
{
login: { screen: MainLoginScreen },
signup: { screen: SignupScreen },
profileScreen: { screen: ProfileScreen },
main: {
screen: createDrawerNavigator({
Home: createStackNavigator(
{
map: {
screen: MapScreen,
headerMode: 'screen',
navigationOptions: {
headerVisible: true,
headerTransparent: false,
headerLeft: (
<View style={{ position: 'absolute', left: 10, display: 'flex', zIndex: 11550 }}>
<Icon
raised
name='bars'
type='font-awesome'
color='rgba(255, 255, 255, 0)'
reverseColor='#444'
onPress={() => { console.log("press"); navigation.goBack() }}
reverse
/>
</View>
)
}
},
history: { screen: HistoryScreen },
foundItem: { screen: FoundItemScreen },
}
),
Settings: {
screen: SettingsScreen,
navigationOptions: ({ navigation }) => ({
title: 'Edit Profile',
})
}
}, {
contentComponent: customDrawerComponent,
drawerWidth: width * 0.8
}
)
}
}, {
headerMode: 'screen',
navigationOptions: {
headerTransparent: true,
headerLeftContainerStyle: { paddingLeft: 20 }
}
}
);
The icon from the screenshot is inside headerLeft.
I've also tried various zIndex values, but with no luck.
Thanks in advance!
Edit:
The drawer has the same issue on the first item, press events don't work on the full area of the drawer item when it's over the header:
https://www.dropbox.com/s/krva5cgp7s59d13/drawer_opened.png?dl=0
Try to wrap Icon inside some Touchable Element like TouchableOpacity/TouchableHighlight and then add an onPress prop to that element
You can get the onPress event in navigationOption's header using navigation params like
static navigationOptions = ({ navigation }) => ({
headerLeft: (
<TouchableOpacity onPress={() => navigation.state.params.handleSave()} >
<Image source={ImageSource} />
</TouchableOpacity>
)
})
Then set the navigation params from the componentDidMount like:
componentDidMount() {
this.props.navigation.setParams({ handleSave: this.onSavePress.bind(this)})
}
then can call the method like:
onSavePress() {
// Do Something
}

How to hide navigation header after adding in nested screens react-native

I have Main screen in which there is a headerbar also this screen contain 2 tabs name Home and Settings , these two tabs have their own header bar their is a button inside my Home Screen which when click calls Details Screen from this Detail Screen i want to hide Main Screen header bar
Code for MainScreen HeaderBar
const TabScreen = createStackNavigator({
TabScreen: {
screen: RootStack,
navigationOptions: {
headerTitle: 'Tabs'
},
},
});
Code For Tabs
const RootStack = createMaterialTopTabNavigator(
{
Home: {
screen: HomeRoot,
navigationOptions : {
tabBarLabel: 'Home',
tabBarIcon: <Image source={{uri:
'https://png.icons8.com/Home/ultraviolet/50/3498db'}} style={{width:20,
height: 20}}/>
},
},
Settings: {
screen: Settings,
navigationOptions : {
tabBarLabel: 'Setting',
tabBarIcon: <Image source={{uri:
'https://png.icons8.com/Home/ultraviolet/50/3498db'}} style= .
{{width:20, height: 20}}/>
},
},
},
Code for Home and Details Screen
const HomeRoot = createStackNavigator(
{
Home: {
screen: Home,
},
Details: {
screen: Details,
},
},
{
initialRouteName: 'Home',
}
);
Is this what you are looking for?
headerMode: 'none',

React-Navigation Drawer and Static Tab-Navigation

i am switching from react navigator to react-navigation and i am actually fine with this approach, but struggeling with one issue.
I want to use a drawer navigation and a bottom aligned Tab-Navigation.
This part is working as expected - no issues here.
I want to have the tabbed navigation fixed with 3 Buttons that will have the same action all over the app. ( eg. dashboard / search / favourites )
From Dashboard you can navigate one level deeper. As i am doing it now, the Label of the Tab formerly "Dashboard" changes to the Name of the navigated-to Item.
to clarify, i added a stack-navigation in the Dashboard-Screen-Tab, so the user can navigate through that pages.
How can i prevent the tabs' labes and actions to change while navigating within the tabs' stack?
Basically i want a fixed Tab Navigation on each screen.
Should i create a fixed View-Component to achieve that?
Here is my setup:
App.js
const MetaTabNavigator = TabNavigator({
Dashboard: {
screen: MachineNavigator
},
Search: { screen: SearchScreen },
Favourites: { screen: FavouritesScreen },
},
{
tabBarPosition: Platform.OS === "ios" ? 'bottom' : 'top',
animationEnabled: true,
tabBarOptions: {
activeTintColor: STYLES.HIGHLIGHT_COLOR
},
swipeEnabled: false,
backBehavior: 'none'
});
const MetaDrawerNavigator = DrawerNavigator({
Home: {
screen: MetaTabNavigator,
navigationOptions: {
drawer: {
label: 'Drawer',
icon: ({ tintColor }) => <Icon name="rocket" size={24} />
},
},
}
},
{
contentComponent: props => <Menu {...props} />
}
);
AppRegistry.registerComponent('myApp', () => MetaDrawerNavigator);
MachineNavigator
const MachineNavigator = StackNavigator({
Main: {
screen: MachineOverview,
navigationOptions: ({ navigation }) => ({
title: "Dashboard",
headerLeft: (
<TouchableOpacity onPress={() => navigation.navigate("DrawerOpen")}>
<IOSIcon name="ios-menu" size={30} />
</TouchableOpacity>
),
headerStyle: { paddingRight: 10, paddingLeft: 10 }
})
},
Category: {
screen: Category,
navigationOptions: (props) => ({
title: "Kategorie",
})
},
ItemDetail: {
screen: ItemDetail,
navigationOptions: (props) => ({
title: "Video",
})
}
})
export default MachineNavigator;
According to this issue, you can add to the tab configuration the tabBarLabel property to control the label:
const MetaTabNavigator = TabNavigator({
Dashboard: {
screen: MachineNavigator, navigationOptions: {tabBarLabel: 'Dashboard'}
},
...