How can i hide a tab in bottom navigation - react native - reactjs

Can someone please advise how can i hide a tab from display in Bottom Navigation, i want to jumpTo the tab from code but i don't want it show in the Bottom Navigation. Below is some code i am using, i just want to hide the vehicle_edit tab from bottom but i want to keep using it in my code via jumpTo,
please if someone can advise.
//routes
const [routes] = React.useState([
{ key: "dashboard", title: "Dashboard", icon: "home" },
{ key: "notifications", title: "Notifications", icon: "bell" },
{ key: "account", title: "My Account", icon: "account" },
{ key: "vehicle_edit", title: "Vehicles", icon: "car" },
{ key: "appointments", title: "Appointments", icon: "calendar" },
]);
//set bottom tab to first always
useEffect(() => {
setIndex(0);
}, []);
//screens
const renderScene = BottomNavigation.SceneMap({
dashboard: DashboardScreen,
notifications: NotificationsScreen,
account: AccountScreen,
vehicle_edit: VehicleEditScreen,
appointments: AppointmentsScreen,
});
//render
return <BottomNavigation navigationState={{ index, routes }} onIndexChange={setIndex} renderScene={renderScene} />;

react-native-paper Bottom Navigation component design for only for Bottom navigation screen.
You need stack navigation for vehicle_edit. You can simply achieve this by using React navigation stack navigator

Brilliant thanks that solved it..
const Stack = createStackNavigator();
const Account = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Account" component={AccountScreen} />
<Stack.Screen name="VehicleEdit" component={VehicleEditScreen} />
</Stack.Navigator>
);
};
//screens
const renderScene = BottomNavigation.SceneMap({
dashboard: DashboardScreen,
notifications: NotificationsScreen,
account: Account,
appointments: AppointmentsScreen,
});

Related

How to reset nested navigators (react-navigation v5)

Having two sets of stack navigators;
const SetOneScreens = () => (
<Stack.Navigator initialRouteName="AScreen">
<Stack.Screen name="AScreen" component={AScreen} />
<Stack.Screen name="BScreen" component={BScreen} />
</Stack.Navigator>
);
const SetTwoScreens = () => (
<Stack.Navigator initialRouteName="CScreen">
<Stack.Screen name="CScreen" component={CScreen} />
<Stack.Screen name="DScreen" component={DScreen} />
</Stack.Navigator>
);
Which are nested in a Drawer navigator
<NavigationContainer>
<Drawer.Navigator initialRouteName="SetOneScreens">
<Drawer.Screen name="SetOneScreens" component={SetOneScreens} />
<Drawer.Screen name="SetTwoScreens" component={SetTwoScreens} options={{swipeEnabled: false}} />
</Drawer.Navigator>
</NavigationContainer>
I want to navigate From BScreen to DScreen and reset the stack (in order to not letting hardware back button in android get back to BScreen)
As in the nesting situation, we should first define the navigator name; how should I define the screen in reset action.
// For navigation
props.navigation.navigate('setTwoScreens',{screen:'DScreen'})
// For reset I can only navigate to initial screen
props.navigation.reset({index:0,routes:[{name:'setTwoScreens'}]})
How should I handle the reset with navigation or CommonActions
As written in the documentation of react-navigation-v5, you need to dispatch CommonAction with reset-action to clear back-stack of your application, so that application doesn't go back to previous screen when user press hardware back-button of device, check below example,
import { CommonActions } from "#react-navigation/native";
props.navigation.dispatch({
CommonActions.reset({
index: 0,
routes: [{ name: "AnotherStackNavigator" }]
})
});
Or if you want to reset to specific screen in that StackNavigator you can do like this:
props.navigation.dispatch({
CommonActions.reset({
index: 0,
routes: [
{
name: "AnotherStackNavigator",
state: {
routes: [
{
name: "AnotherStackNavigatorScreen",
params: {
...
}
}
]
}
}
]
})
});
I tried the solution from Aditya, but had issues with the back button not going back to the root of the new stack.
This seemed to fix that issue for me though:
props.navigation.dispatch({
CommonActions.reset({
index: 0,
routes: [
{
name: "AnotherStackNavigator",
state: {
routes: [
{
name: "AnotherStackNavigatorRootScreen"
},
{
name: "AnotherStackNavigatorScreen",
params: {
...
}
}
]
}
}
]
})
});

Main Navigation 3 (Drawer and Stack) together problem

I am using react navigation version 3
I trying to use Drawer and Stack together
I am implementing a navigation drawer and works well
But when I use stack navigation I can't see the back button option in the header
Drawer -> ListScreen ->onpress item open stack navigation to view profile ->
here I can't go back to ListScreen
I tried edit navigation option and add custom properties but still, I don't understand why it's not visible
const AuthStackNavigation = createStackNavigator({
HomeStack: {
screen.
screen: LeadScreen,
headerBackTitleVisible:true,
navigationOptions: ({ navigation }) => ({
headerLeft: <Icon
name='arrow-back' onPress={ () => { navigation.goBack() }} style={{width:100}} />
,
title: `${navigation.state.params.name}'s Profile'`,
headerStyle: {
backgroundColor: '#f4511e',
},
headerTitleStyle: { flex: 1, textAlign: 'center'},
}),
},
});
//navigate('Profile', { name: 'Brent' })
const AppStackNavigator = createDrawerNavigator({
'בית':HomeScreen,
'רשימת לידים':LeadsScreen,
'ניהול סוכנים':ManageUsersScreen
,
'הגדרות':SettingsScreen
},{
contentComponent:CustomerDrawerComponent
}
)
const MainNavigation = createSwitchNavigator({
HomeDrawer: AppStackNavigator,
AuthStack: AuthStackNavigation, // You will use this.props.navigation.replace('HomeDrawer') after login process.
})
export default createAppContainer(MainNavigation);

onpress event for icon in TabNavigator (react native with react-navigation)

I have an icon that i include in a tab navigator as per below:
const SearchTabBarIcon = ({ tintColor }) => (
<IconMCI
name="account-search"
size={45}
color={tintColor}
onPress={() => console.log('HELP!!')}
/>
);
....then my tabNavigator looks like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
The console.log is fired when I click the icon, however the native functionality of the tabNavigator is lost.
How can I fire the onPress event but also maintain the navigation functionality?
Ive heard I should perhaps "add the props of navigation to my component in the render".
The render function of App.js looks like this:
render() {
const Layout = createRootNavigator(this.props.isAuthenticated);
return (
<View style={{ flex: 1, backgroundColor: '#F8F8FF' }}>
<Layout />
</View>
);
}
....and the function createRootNavigator looks like this
const createRootNavigator = (authenticated = false) =>
createSwitchNavigator(
{
SignedInTabNav: {
screen: SignedInTabNav,
},
SignedOutStackNav: {
screen: SignedOutStackNav,
},
},
{
initialRouteName: authenticated ? 'SignedInTabNav' : 'SignedOutStackNav',
},
);
....so where/how do I add the navigation prop in the render for App.js?...and will it be passed down through createRootNavigator.....and further through createSwitchNavigator....and finally passed into SignedInTabNav (TabNavigator) where the Search icon is referenced?
You can simply use tabBarOnPress callback to handle press events as an additional option, like this:
const SignedInTabNav = TabNavigator(
{
Profile: {
screen: Profile,
navigationOptions: {
tabBarLabel: 'Me',
tabBarIcon: ProfileTabBarIcon,
},
},
Search: {
screen: Search,
navigationOptions: {
tabBarLabel: 'Search',
tabBarIcon: SearchTabBarIcon,
tabBarOnPress: ({ navigation, defaultHandler }) => {
console.log('this will be fired just before nagivation happens')
defaultHandler() // if you omit this, navigation will not happen
}
},
},
tabBarComponent: TabBarBottom,
animationEnabled: false,
},
);
Solution was to use the 'didFocus' event from reactNavigation
https://reactnavigation.org/docs/en/function-after-focusing-screen.html
componentDidMount() {
const { navigation } = this.props;
this.focusListener = navigation.addListener('didFocus', () => {
// The screen is focused
// Call any action
});
}

Nested TabNavigator inside StackNavigator: controlling the header

I have a setup much like this:
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
})
The stack navigator has a header, and that's fine. What I want is to get different header icons depending on what tab I'm currently on.
I'm using the following versions:
"react": "16.3.1",
"react-native": "~0.55.2",
"react-navigation": "^2.2.5"
I've considered switching up my setup so that each tab screen has its own StackNavigator but I like having the sliding animation when switching tabs and I don't want the header icons to slide along. The header bar should remain static but simply show different icons depending on the current tab.
You can use like this below, https://reactnavigation.org/docs/en/stack-navigator.html
//Screen1 Stack.
const Screen1 = createStackNavigator ({
Home: {
screen: Home,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
//Screen2 Stack
const Screen2 = createStackNavigator ({
Profile: {
screen: Profile,
navigationOptions: {
header: null //Need to set header as null.
}
}
});
let Tabs = createMaterialTopTabNavigator({
Screen1:{
screen: Screen1 //Calling Screen1 Stack.
},
Screen2:{
screen: Screen2 //Calling Screen2 Stack.
}
},{ tabBarPosition: 'bottom' }) //this will set the TabBar at Bottom of your screen.
let Stack = createStackNavigator({
tabs:{
screen: Tabs, //You can add the NavigationOption here with navigation as parameter using destructuring.
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
//you can use switch case,on matching the route name you can set title of the header that you want and also header left and right icons similarly.
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
},
otherScreen:{
screen: OtherScreen
}
})
//navigationOptions
navigationOptions: ({navigation})=>{
//title: (navigation.state.routes[navigation.state.index])["routeName"]
//this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header.
//use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.
//title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
case "Screen1":
return {
title: "Home",
headerLeft: (<Button
onPress={()=> alert("hi")} //Here you can able to set the back behaviour.
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
case "Screen2":
return {
title: "Profile",
headerLeft: (<Button
onPress={()=> alert("hi")}
title="Back"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/> ),
headerRight: <Button title= "Right"/>
}
default:
return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
}
}
//alert(navigation.state)
{
"routes":[
{
"key":"Screen1",
"routeName":"Screen1",
"routes":[
{
"key":"Home",
"routeName":"Home",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
},
{
"key":"Screen2",
"routeName":"Screen2",
"routes":[
{
"key":"Profile",
"routeName":"Profile",
}
],
"index":0,
"isTransitioning":false,
"key":"id-1530276062643-0"
}
],
"index":0,
"isTransitioning":false,
"routeName":"tabs",
"key":"id-1530276062643-0"
}
//(navigation.state.routes[navigation.state.index])["routeName"]
//(navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName
this will give the current route name of the tab inside StackNavigation.
The above code will set the title in root stack header where the TabBar resides as first route,so we are setting the header as null for the individual stack in TabBar. By using this way will provide Animation while switching the screens in TabBar since header will remain static.
you can find the working copy here https://www.dropbox.com/s/jca6ssn9zkzh9kn/Archive.zip?dl=0
Download this and Execute the following.
npm install //to get dependencies
react-native upgrade //to get android and ios folder
react-native link //to link dependencies and libraries
react-native run-ios (or) react-native run-android
you can use the above, Let me know if any.
You can achieve the desired behavior with the current navigation stack configuration of yours. You might need to change couple of things and combine couple of properties but it is fairly simple when you get yor head around it.
I try to explain it with a small example.
Consider having below navigators;
const Tabs = createBottomTabNavigator({
screen1: Tab1,
screen2: Tab2
})
const Stack = createStackNavigator({
tabs: {
screen: TabsPage,
navigationOptions: ({navigation}) => {
return { title: (navigation.state.params && navigation.state.params.title ? navigation.state.params.title : 'No Title' ) }
}
},
otherScreen: Page
})
As you can see I am setting the title parameter from the navigation state. To be ale to set the param for that navigator, we are going to get help from screenProps property;
class TabsPage extends Component {
onTabsChange = (title) => {
this.props.navigation.setParams({ title })
}
render() {
return(<Tabs screenProps={{ onTabsChange: this.onTabsChange }} />)
}
}
I created a wrapper component for tab navigator and passed down a function which is setting the title parameter.
For the last part we need to know how and when to use that function we passed down. For that we are going to use addListener navigation prop
class Tab1 extends React.Component {
setTitle = () => {
this.props.screenProps.onTabsChange('Title from Tab 1')
}
componentDidMount() {
this.props.navigation.addListener('willFocus', this.setTitle)
}
render() {
return <View><Text>{'Tab1'}</Text></View>
}
}
When our tab focused the passed function will run and then set the title for that tab. You can use this process for setting different buttons or icons for the header. You can find the working snack here.
const RootStack = createStackNavigator(
{
Home: {screen: HomeScreen},
FilterScreen: createMaterialTopTabNavigator({
Tab1: {screen:Tab1Screen},
Tab2: {screen: Tab2Screen},
}),
},
{
mode: 'modal',
headerMode: 'none',
}
);
render() {
return <RootStack/>;
}
In AppNavigation.js //or where you have defined your routes.
let Tabs = createBottomTabNavigator({
screen1: Screen1,
screen2: Screen2
})
let Stack = createStackNavigator({
tabs: Tabs
otherScreen: OtherScreen
},{
headerMode:"float", //Render a single header that stays at the top and animates as screens are changed. This is a common pattern on iOS.
headerTransitionPreset:"fade-in-place" //this will give a slight transition when header icon change.
}
)
In Screen1.js
class Screen1 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen1"}/>,
...
}
}
...
}
NOTE: You can add title, headersStyle,headerRight in same way read this link header configuration for more detail
In Screen2.js do similar to Screen1
class Screen2 extends Component {
static navigationOptions = ({ navigation }) => {
return {
...
headerLeft: <HeaderLeftImage navigation={navigation} image={"Image_For_Screen2"}/>,
...
}
}
...
}
Header.js
export const HeaderLeftImage = (props) => (
<View style={{
'add styles'
}}>
<TouchableOpacity onPress={() => {"Add action here" }}>
<Image source={props.image} resizeMethod='resize' style={{ height: 30, width: 90, resizeMode: 'contain' }} />
</TouchableOpacity>
</View>
)
Hope this helps, if you have any doubt regarding the code feel free to ask.
If you are using react navigation <2 i.e ~1.5.* You can set it like this.
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { title: "Tab 1 Heading", tabBarLabel:"Tab 1 "}
},
}
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { title: "Tab 2 Heading", tabBarLabel:"Tab 2 "}
}
}
})
const Stack = StackNavigator({
tabs: {
screen: Tabs
navigationOptions: ({navigation}) => {
return { title: "Stack"}
}
},
otherScreen: Page
})
I'm not sure why did they remove this feature, when you try the same it won't work on the latest react-navigation. Now title object key is used for fallback it seems.
This may be helpful some users. If you wish you can try downgrading also.
I have the Upgraded the React Navigation for my project I think this way will be useful to someone
const Tabs = TabNavigator({
Tab1:{
screen: Tab1,
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 1 "}
}},
Tab2:{
screen: Tab2
navigationOptions: ({navigation}) => {
return { tabBarLabel:"Tab 2 "}
}}
});
Tabs.navigationOptions = ({navigation})=>{
const { routeName } = navigation.state.routes[navigation.state.index]; //This gives current route
switch(routeName){
case "Tab1":
headerTitle="Tab 1";
break;
case "Tab1":
headerTitle="Tab 1";
break;
}
return {
headerTitle: headerTitle
}
}
You can simply use property headerShown: false. I have created a working example of react-navigation version: 5.x.x, nesting different navigators: stack, drawer & bottom-tabs.
Here's the link to github:
https://github.com/mvpbuddy/react-native-nested-navigators/
For a more detailed overview, you can checkout my blog:
https://mvpbuddy.io/blog/detail/how-to-build-an-app-with-nested-stack-drawer-bottom-tab-navigators

using of Custom Tabs with StackNavigator?

I have list of users , each user has its display image in the list.
What I am trying is whenever user presses the display image , get redirected to his/her profile through stackNavigation .
CustomTabView:
const CustomTabView = ({ router, navigation }) => {
const { routes, index } = navigation.state;
const ActiveScreen = router.getComponentForState(navigation.state);
const routeNav = addNavigationHelpers({
...navigation,
state: routes[index],
});
const routeOptions = router.getScreenOptions(routeNav, 'tabBar');
return (
<View style={styles.container}>
<CustomTabBar
navigation={navigation}
activeRouteName={routes[index].routeName}
icon={routeOptions.tabBarIcon}
/>
<ActiveScreen
navigation={addNavigationHelpers({
...navigation,
state: routes[index]
})}
/>
</View>
);
};
StackNavigator: // goToProfile.js // also tried placing in index.anndroid.js but didnt found a way to export
const goToProfile = StackNavigator({
Profile: {
screen: Profile,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.person.name.first} ${navigation.state.params.person.name.last}`
})
},
})
custom tabs: //index.android.js
const CustomTabRouter = TabRouter(
{
Chat: {
screen: Chats,
path: ""
},
Status: {
screen: Contacts,
path: "notifications"
},
Camera: {
screen: Camera,
path: "settings"
}
},
{
initialRouteName: "Chat",
},
);
const CustomTabs = createNavigationContainer(
createNavigator(CustomTabRouter)(CustomTabView)
);
Also my component :
<TouchableOpacity
onPress = { () => this.props.navigation.navigate('Profile', { item } ) } >
<Avatar
source={{ uri: item.picture.thumbnail }}
/>
</TouchableOpacity>
Your Stack Navigator will look like this:
const ChatStack= StackNavigator({
Chat:{screen: Chat,
navigationOptions: {
header: null,
}},
Profile: {
screen: Profile,
navigationOptions: ({ navigation }) => ({
title: `${navigation.state.params.person.name.first} ${navigation.state.params.person.name.last}`,
tabBarVisible: false
// depending if you want to hide the tabBar on the profile page if not remove it.
})
},
})
Then Your customTab will take this shape:
const CustomTabRouter = TabRouter(
{
Chat: {
screen: ChatStack,
},
Status: {
screen: Contacts,
path: "notifications"
},
Camera: {
screen: Camera,
path: "settings"
}
},
.........
With those changes you should be able to navigate to Profile screen with your
this.props.navigation.navigate('Profile', { item } )
You want to dispatch a navigation action, not render a new stack - Although I'm not sure your navigators are properly constructed for this to succeed...
_goToProfile = (person) => {
<goToProfile navigate={this.props.navigation} />
}
should be
_goToProfile = (person) => {
this.props.navigation.navigate('Profile', person)
}

Resources