Custom close button inside the header of StackNavigator of React Navigation - javascript

I'm currently working with React Native and there I've got a question about React Navigation and the StackNavigator. I would like to add a custom close button to the header, but I'm not sure how to do this.
It's just possible for me to navigate to the screens, but not for example to go back or dispatch something.
On my example the navigationOptions for the HomeScreen is working. The drawer opens and the SettingsButton navigates to the settings screen. But I've got problems with the navigationOptions of the EntryScreen. There I build a CloseButton to go back, but I need access to the navigation object.
With navigate it's possible to navigate to other screens, but I need access to the parent object navigation. For example on the class for the drawer it's possible to get access through props.navigation (For example props.navigation.navigate('Home') or props.navigation.goBack(null))
This is a part of my current code. Of course it's currently not working because of the missing access to navigation:
const Stack = StackNavigator({
Home: {
screen: HomeScreen,
navigationOptions: {
header: ({ navigate }) => ({
left: <DrawerButton navigate={navigate} />,
right: <SettingsButton navigate={navigate} />
})
}
},
Entry: {
screen: EntryScreen,
navigationOptions: {
header: ({ navigation }) => ({
right: <CloseButton navigate={navigation } />
})
}
}
})
export const Drawer = DrawerNavigator({
Home: {
screen: Stack
}},
{
contentComponent: HamburgerMenu
}
)
These are the buttons:
export const CloseButton = (props) => {
let testButton = <TouchableHighlight onPress={() => props.navigation.goBack(null)}>
<Icon name='close' style={styles.headerButtonIcon} />
</TouchableHighlight>
return testButton
}
export const SettingsButton = (props) => (
<TouchableHighlight onPress={() => props.navigate('Settings')}>
<Icon name='more-vert' style={styles.headerButtonIcon} />
</TouchableHighlight>
)
Maybe you can help me there. Thanks in advance!

I've found the answer thanks to the help of sigmazen on Github.
Instead of navigation I have to put goBack into the header for the CloseButton. After this I'm able to use it through props.goBack(null)
Entry: {
screen: EntryScreen,
navigationOptions: {
title: `Eintrag`,
header: ({ goBack }) => ({
right: <CloseButton goBack={goBack} />
})
}
},
export const CloseButton = (props) => {
let test = <TouchableHighlight onPress={() => props.goBack(null)}>
<Icon name='close' style={styles.headerButtonIcon} />
</TouchableHighlight>
return test
}

Related

Expo React Native Drawer Navigator Logout functionality

StackOverflow I am very new to react native since I implement drawer navigation now I want to include a logout button at the end of the drawer but I don't find how to do that any good practice and ideas about how to achieve this kind of functionality. this is my code for drawer I find it from hours of google and it works fine but it has functions of screens I don't find any option of how to make a logout link in this code if this code is not correct then suggest any other good snippet thanks in advance
import React from 'react';
import { Ionicons } from '#expo/vector-icons'
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import { createDrawerNavigator, createStackNavigator, createAppContainer } from 'react-navigation';
import { DrawerActions } from 'react-navigation-drawer';
// _signOutAsync = async () => {
// await AsyncStorage.clear();
// this.props.navigation.navigate('Auth');
// };
const HomeScreen = () => (
<View style={styles.container}>
<Text>Home Screen!</Text>
</View>
);
const ProfileScreen = () => (
<View style={styles.container}>
<Text>Profile Screen!</Text>
</View>
);
const SettingsScreen = () => (
<View style={styles.container}>
<Text>Settings Screen!</Text>
</View>
);
const DrawerNavigator = createDrawerNavigator({
Home: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => ({
title: 'Home Screen',
drawerLabel: 'Home',
drawerIcon: () => (
<Ionicons name="ios-home" size={20} />
)
})
},
Profile: {
screen: ProfileScreen,
navigationOptions: ({ navigation }) => ({
title: 'Profile Screen',
drawerLabel: 'Profile',
drawerIcon: () => (
<Ionicons name="ios-person" size={20} />
)
})
},
Settings: {
screen: SettingsScreen,
navigationOptions: ({ navigation }) => ({
drawerIcon: () => (
<Ionicons name="ios-settings" size={20} />
)
})
},
});
const StackNavigator = createStackNavigator({
DrawerNavigator: {
screen: DrawerNavigator,
navigationOptions: ({ navigation }) => {
const { state } = navigation;
if(state.isDrawerOpen) {
return {
headerLeft: ({titleStyle}) => (
<TouchableOpacity onPress={() => {navigation.dispatch(DrawerActions.toggleDrawer())}}>
<Ionicons name="ios-close" style={styles.menuClose} size={36} color={titleStyle} />
</TouchableOpacity>
),
}
}
else {
return {
headerLeft: ({titleStyle}) => (
<TouchableOpacity onPress={() => {navigation.dispatch(DrawerActions.toggleDrawer())}}>
<Ionicons name="ios-menu" style={styles.menuOpen} size={32} color={titleStyle} />
</TouchableOpacity>
),
}
}
}
}
})
export default createAppContainer(StackNavigator);
You can create one content component to render inside your Drawer Navigator, making it easier to modify.
I will explain with your example (I am assuming that it is your App.js file):
//import CustomDrawer from '...'
const DrawerNavigator = createDrawerNavigator({
Home: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => ({
title: 'Home Screen',
drawerLabel: 'Home',
drawerIcon: () => (
<Ionicons name="ios-home" size={20} />
)
})
},
Profile: {
screen: ProfileScreen,
navigationOptions: ({ navigation }) => ({
title: 'Profile Screen',
drawerLabel: 'Profile',
drawerIcon: () => (
<Ionicons name="ios-person" size={20} />
)
})
},
Settings: {
screen: SettingsScreen,
navigationOptions: ({ navigation }) => ({
drawerIcon: () => (
<Ionicons name="ios-settings" size={20} />
)
})
},
//Here you will add one more pair of curly brackets to add more configs.
}, {
initialRouteName: 'Home',
contentComponent: CustomDrawer //This is the option that will allow you to add the button
});
You will create one entire component to modify like you want and then render inside the Drawer Navigator. Remember, I am using CustomDrawer name, you will import this component inside your App.js file.
import React from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { Button } from 'react-native-elements';
import { DrawerNavigatorItems } from 'react-navigation-drawer';
const CustomDrawer = ({ ...props }) => {
return (
<>
<View>
<DrawerNavigatorItems
{...props}
itemsContainerStyle={{}}
itemStyle={{}}
/>
</View>
<View
style={{
flexDirection: 'row',
alignSelf: 'center',
position: 'relative',
marginBottom: 20,
}}
>
<Button
title={'Log out'}
buttonStyle={{ width: 200, borderRadius: 20 }}
onPress={}
/>
</View>
</>
);
};
const styles = StyleSheet.create({});
export default CustomDrawer;
Here I am rendering only the CustomDrawer props, that is the itens that you create in your App.js and rendering it (specifically it is the ...props that I am passing in DrawerNavigationItems, so you can customize it like you want, like one normal screen, place buttons, create views and apply styles to it.
You can also instead of creating one new screen to render inside your Drawer Navigator code it inside your App.js, but personally I feel it much messed up
You can learn more with this tutorial

Combine createStackNavigator and createBottomTabNavigator?

Scenario :
I have an app working with three tabs for navigation (School, Admin, Family);
I am now trying to add in other screens, that will not have corresponding tabs. These screens will be navigated to using something like this.props.navigation.navigate('ChatScreen')
Issue
- With my past solution, any time I added a screen it would add a tab for that screen.
Todo :
I would like to have the tabs in my navigation stack, as well as other normal (not-tab) screens.
I would like both the tabs and the header to be persistent
I have been unsuccessful at combining both, and have tried many
variations of the code below.
Tried Code :
const School = createStackNavigator({
School: {
screen: SchoolScreen,
navigationOptions: {
headerTitle: <CustomHeaderTitle screen='school'/>
}
}
});
const Admin = createStackNavigator(
{ Admin: {
screen: AdminScreen,
navigationOptions: {
headerTitle: <CustomHeaderTitle screen='admin' />
}
}
});
const Family = createStackNavigator(
{
Family: {
screen: FamilyScreen,
navigationOptions: {
headerLeft: null,
headerTitle: <CustomHeaderTitle screen='family' />
}
}
}
);
const ChatStack = createStackNavigator({
CreateChat: CreateChatScreen
});
const TabStack = createBottomTabNavigator(
{
School: School,
Admin: Admin,
Family: Family
},
{
navigationOptions: ({ navigation }) => ({
tabBarIcon: () => {
const { routeName } = navigation.state;
return <Image id={1} source={require('./app/img/school_logo.png')} />
},
tabBarLabel: navigation.state.routeName
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
style: {
backgroundColor: 'black',
height: 55
}
}
}
);
const RootStack = createStackNavigator({
Root: ChatStack,
Tabs: TabStack
})
export default class App extends Component {
render() { return (
<Provider store={store}>
<RootStack />
</Provider>
);
}
}
I apologize, I cannot get this code to format after fighting with it for some time.
Thank you for any help or recommendations in advance!
Please suggest.
Credit to an unnamed redditor:
You'll have to nest you're entire stack into each screen of the tab navigator. As far as I know you can't access different screens in a StackNavigator if they are nested inside a different TabNavigator screen.
For example, if you want to be able to navigate to the chat screen from the SchoolScreen, you'll have to include that component inside your School navigator.
const School = createStackNavigation({
School: {
screen: SchoolScreen
},
SchoolChat: {
screen: CreateChatScreen
}
});
From there your main TabNavigator should look about the same
const TabStack = createBottomTabNavigator({
School: School
});
you should hide the RootStack header when TabStack is focused
TabStack.navigationOptions = {
// Hide the header from root stack
header: null,
};
and you did not need add stack to CreateChatScreen
const RootStack = createStackNavigator({
Tabs: TabStack,
ChatScreen: CreateChatScreen,
})
In react native navigation 5
import React from 'react';
import {Text} from 'react-native';
import {createStackNavigator} from '#react-navigation/stack';
import {NavigationContainer} from '#react-navigation/native';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function Scr(){
return <Text>hello</Text>;
}
function MyTabs() {
return (
<Tab.Navigator
initialRouteName="Expolre"
tabBarOptions={{
activeTintColor: '#414757',
}}>
<Tab.Screen name="Expolre" component={Scr} />
</Tab.Navigator>
);
}
export default function Routing() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="login"
component={Scr}
options={{header: () => null}}
/>
<Stack.Screen
name="dashboard"
component={MyTabs}
options={{header: () => null}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}

How to open Side Menu from nested StackNavigator in React Native?

My navigation has a following structure:
StackNavigator
Login
PasswordReset
Dashboard
VehicleEnquiry
VehicleDetails
MainRouter.js file:
const RootDrawer = DrawerNavigator({
Dashboard: {
screen: DashboardScreen,
navigationOptions: {
drawerLabel: Dashboard,
drawerIcon: () => (
<Icon name="home" style={styles.drawerIconStyle} />
),
},
},
VehicleEnquiry: {
screen: VehicleEnquiryScreen,
navigationOptions: {
drawerLabel: 'Barcode Scanner',
drawerIcon: () => (
<Icon name="barcode" style={styles.drawerIconStyle} />
),
},
},
Logout: {
screen: LoginScreen,
navigationOptions: {
title: "Login",
drawerLabel: 'Logout',
drawerIcon: () => (
<Icon name="arrow-left" style={styles.drawerIconStyle} />
),
headerLeft: null
},
},
},
);
RootDrawer.navigationOptions = ({ navigation }) => ({
headerTitleStyle: styles.headerTitleStyle,
headerStyle: styles.headerStyle,
});
const MainRouter = StackNavigator({
Dashboard: {
screen: RootDrawer,
},
Home: {
screen: HomeNavigation,
},
VehicleEnquiry: {
screen: VehicleEnquiryScreen,
},
VehicleDetails: {
screen: VehicleDetailsScreen
}
});
In the body of VehicleEnquiryScreen component I have added static navigationOptions just after the constructor of the class:
static navigationOptions = ({ navigation }) => ({
title: "Dashboard",
headerLeft: (
<TouchableOpacity onPress={() => { this.props.MainRouter.navigation.navigate('DrawerOpen'); }} >
<View style={styles.userImageBackground}>
<Icon name="bars" style={{ color: 'white', padding: 10, marginLeft: 10, fontSize: 20 }} />
</View>
</TouchableOpacity>
),
})
This is what I want to achieve:
I run the app and go straight to the Login screen. I sign in and go to the dashboard screen. From the dashboard I open left side menu and pick the option Vehicle Enquiry and go to this screen. I want to be able to open the side menu once again from the Vehicle Enquiry screen but when do this I just go to the previous screen instead of seeing the menu.
From Vehicle Enquiry Screen I can go to the Vehicle Details Screen. I would also like to be able to open the side menu when I’m on Vehicle Details Screen.
I’ve tried many ways to solve this issue based on:
https://github.com/react-navigation/react-navigation/issues/335
https://reactnavigation.org/docs/intro/headers#Header-interaction-with-screen-component
Any help will be appreciated.
Cheers

React Native TabNavigator, Cannot read property 'navigate' of undefined

I've just set up a React Navigation TabNavigator which works fine but I'm having issues with transitions between my views.
I get the following error message from trying to redirect the user to another page after a click on a button.
Cannot read property 'navigate' of undefined
I'm super confused on how to make this work.
Here is the navigator:
import React from 'react';
import { TabNavigator, StackNavigator } from 'react-navigation';
import ElemList from './src/components/ElemList';
import ElemShow from './src/components/ElemShow';
const RootTabs = TabNavigator({
Home: {
screen: ElemList,
navigationOptions: {
tabBarLabel: 'Home',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
),
},
main: {
screen: StackNavigator({
show: { screen: ElemShow },
})
}
}
});
Here is my code:
onRowPress(data) {
this.props.navigation.navigate('ElemShow', {id: data})
};
render() {
const { key, elem } = this.props;
return (
<TouchableWithoutFeedback onPress={() => this.onRowPress(elem)}>
<View style={styles.viewStyle} key={key}>
<Text>Here is the info</Text>
</View>
</TouchableWithoutFeedback>
)
}
The problem is with this line
this.props.navigation
Should be
this.props.navigator
Additionally I don't know that navigate is part of the screens API; typically you will use push or resetTo.

How to use tabBarComponent for TabNavigator? Tab bar not showing

I'm trying to make my own custom tab bar and it seems tabBarComponent is the way to do it by setting as my own component. With the below code my tab bar does not show up.
const TabNav = TabNavigator({
LaunchScreen: {
screen: PrimaryNav,
navigationOptions: {
tabBarLabel:'Find',
tabBarIcon: ({ tintColor }) => (
<Icon name='search' size={20} color='white' />
),
},
},
}, {
navigationOptions: {
headerTintColor: 'grey'
},
tabBarComponent: FooterTabs,
tabBarPosition: 'bottom',
swipeEnabled:false,
animationEnabled:false,
lazy:true,
tabBarOptions: {
showIcon:true,
showLabel:false,
style: {
backgroundColor: 'black'
}
}
});
In FooterTabs.js:
export default class FooterTabs extends Component {
render () {
return (
<View style={styles.container}>
<Text>FooterTabs Component</Text>
</View>
)
}
}
What am I missing?
const TabNav = TabNavigator({
......,
tabBarComponent: props => (
<FooterTabs{...props} />
),
tabBarPosition: 'bottom',
........
});
Try that. enclose your FooterTabs i.e <FooterTabs /> not FooterTabs
After some trial and error, the solution to my issue was to wrap my footer content in a ScrollView, then the tabs showed up as expected, though I am not sure why that is required.
I also implemented Caleb's suggestion (thanks!) of using tabBarComponent: props => <FooterTabs{...props} /> in order to pass the props which I need though was not the cause of the issue.

Categories