Uber-like Drawer navigation in React Native app - react-native

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',
}
);

Related

react-navigation goBack between stack navigators

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

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 native, show a bar above of TabNavigator, who hide on scroll ( like facebook app do)

I'm devolopping an app with react native.
I use tabNavigator and StackNavigator from react-navigation to navigate between tabs.
Now i want to create , exactly like the facebook's app, a tab displayed above my tabNavigator. This tabs hide on scroll down.
The FlatList component have ListHeaderComponent option for rendering his header who also hide when scroll down.
Any idea ?
I didn't found anything on https://reactnavigation.org/docs/ or react-native docs
Looks like a TabNavigator nested inside a StackNavigator, like below
StackNavigator(
{
Tabs: {
screen: TabNavigator(
{
TabA: {
screen: TabA,
navigationOptions: {
tabBarIcon: <MaterialCommunityIcons name={"account"} />
}
},
TabB: {
screen: TabB,
navigationOptions: {
tabBarIcon: <MaterialCommunityIcons name={"message"} />
}
},
TabC: {
screen: TabC,
navigationOptions: {
tabBarIcon: <MaterialCommunityIcons name={"earth"} />
}
}
},
{
tabBarOptions: {
showLabel: false,
showIcon: true,
style: {
backgroundColor: "white"
}
}
}
),
navigationOptions: {
title: "Notifications"
}
}
},
{
navigationOptions: ({ navigation }) => ({
headerRight: <MaterialCommunityIcons name={"magnify"} size={30} style={{ color: "white" }} />,
headerStyle: {
backgroundColor: "rgb(76, 62, 84)"
},
headerTitleStyle: { color: "white" }
})
}
)

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'}
},
...