How to send props from createBottomTabNavigation to every tab screen? - react-native

I am using createBottomTabNavigation and I have 4 screens which I can access by pressing the tab bar screen in the bottom of my screen. I am also using Stack Navigation to display the title of the screen there and also all screens have the same settings icon.
I could make the same function in every screen that takes care of onPress event on the Settings icon but that is repetitive and I don't want to do it.
My question is - is there a way for me to pass a function as a props from the App component which holds the bottom navigation to every screen?
App screen code:
<NavigationContainer >
<Tab.Navigator initialRouteName="Home" tabBarOptions={{
activeTintColor: '#FF9F0A',
inactiveTintColor:'white',
style: {
backgroundColor:'#000000',//color you want to change
borderTopWidth: 0,
paddingTop:5,
paddingBottom:5,
},
}}>
<Tab.Screen name="Home" component={Home} options={{
tabBarLabel: 'HOME',
tabBarIcon: ({ color, size }) => (
<HomeTabIcon name="home" color={color} size={size} />
),
}}/>
<Tab.Screen name="Controls" component={Controls} options={{
tabBarLabel: 'CONTROLS',
tabBarIcon: ({ color, size }) => (
<ControlsTabIcon name="controls" color={color} size={size} />
),
}}/>
<Tab.Screen name="Charging" component={Charging} options={{
tabBarLabel: 'CHARGING',
tabBarIcon: ({ color, size }) => (
<ChargingTabIcon name="charging" color={color} size={size}/>
),
}}/>
Charging screen:
function Charging() {
return (
<View style={globalStyles.container}>
<Text>Charging</Text>
<StatusBar style="auto" />
</View>
);
}
export default function ChargingStackScreen() {
return (
<ChargingStack.Navigator>
<ChargingStack.Screen name="CHARGING" component={Charging} options={{
headerRight: () => (
<View style={globalStyles.headerRight}>
<SettingsIcon />
</View>
),
headerTitleAlign:'left',
headerTintColor: 'white',
headerTitleStyle: globalStyles.headerTitle,
headerStyle: globalStyles.header
}}/>
</ChargingStack.Navigator>
);
}

So what I think you should do is have the header as a separate component, where you can use the useNavigation hook (assuming you're using the latest version of react-navigation), and have the settings button do the navigation you need. Then just render the header component once above the Tab.Navigator instead of rendering it on every screen.
Something like:
function Header(props){
const navigation = useNavigation();
// component code
<SettingsButton onPress={() => navigation.navigate(whatever)}/>
}
And then I am pretty sure (not 100% though) that you can render it in your NavigationContainer, something like:
<NavigationContainer >
<Header/>
<Tab.Navigator initialRouteName="Home" tabBarOptions={{
// rest of the code

Related

Passing props to Tab.Screen within BottomTabNavigator

I am running into difficulty passing props to one of my TabScreens using react-native's BottomTabNavigator. I have tried a couple of different options. Without props the Tab.Screen looks like this:
<Tab.Screen
name='Visits'
component={VisitsTab}
options={{
tabBarLabel: 'Visits',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
/>
So what I tried was this:
<Tab.Screen
name='Visits'
component={(props) => <VisitsTab {...props} />}
options={{
tabBarLabel: 'Visits',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
/>
This still doesn't work, as the prop I am logging out in the VisitsTab is undefined - in other words, the VisitsTab is not receiving props. And to clarify, props is available in the MainScreen that passed on the props. What am I missing here?
By the way, I also tried this, but get the same issue:
<Tab.Screen
name='Visits'
options={{
tabBarLabel: 'Visits',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
>
{(props) => <VisitsTab {...props} />}
</Tab.Screen>
The full navigation for this bottom tab navigator looks like this:
import { VisitsTab } from './tabs/VisitsTab';
import { ChartTab } from './tabs/ChartTab';
import { GoalsTab } from './tabs/GoalsTab';
export const MainScreen = (props) => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator
screenOptions={{
headerShown: true,
title: 'Patient',
headerLeft: () => (
<Feather
name='chevron-left'
size={24}
onPress={() => props.navigation.goBack()}
/>
),
}}
initialRouteName={'Visits'}
>
<Tab.Screen
name='Visits'
component={(props) => <VisitsTab {...props} />}
options={{
tabBarLabel: 'Visits',
tabBarIcon: ({ color, size }) => <FontAwesome name='street-view' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Chart'
component={ChartTab}
options={{
tabBarLabel: 'Charts',
tabBarIcon: ({ color, size }) => <FontAwesome name='id-badge' color={color} size={size} />,
}}
/>
<Tab.Screen
name='Goals'
component={GoalsTab}
options={{
tabBarLabel: 'Edit Goals',
tabBarIcon: ({ color, size }) => <FontAwesome name='trophy' color={color} size={size} />,
}}
/>
</Tab.Navigator>
);
};
By the way, somewhat frustratingly, the documentation page for the Bottom Tabs Navigator doesn't actually address how to pass props.
Props for a component (for a screen to be precise) are not passed by the navigation framework. Props are passed from a parent to its child components. When writing component={Visits}, we do not instantiate an actual JSX components. Thus, there are no props to be passed.
In order to pass information during navigation, we use the route params. Here is an example: navigation.navigate("Visits", {x: 42}).
However, if Visits is supposed to be an initial screen of some navigator, then we have never actually navigated. Thus, the above approach does not help.
For this reason, the react-navigation framework added the initialParams prop for screens. They are defined inside the screen of some navigator.
<Tab.Screen
name='Visits'
component={VisitsTab}
initialParams={props}
/>
whereby props is any object and it might be better to use a different name, since these aren't actual props.
Then, access them in your screen.
function VisitsTab({route}) {
const params = route?.params
return (...)
}
We can access the passed information via params.

React native v6: tab bar customization

i have a bottom tab in my react native application, when i try to edit the tabBarStyle on the tab navigator it doesnt work. It only works on 1 out of my 3 tabs for some reason.
Only the help tab follows the required design the home and the test tab remain as default. I have tried all solutions only add made sure that i am using screenOptions and tabBarStyle instead of style.
const Tab = createBottomTabNavigator();
const AppNavigator = () => {
return (
<Tab.Navigator
screenOptions={{
tabBarActiveTintColor:'white',
tabBarLabelStyle:{
fontWeight:'700'
},
tabBarStyle: {
backgroundColor:'black',
borderTopColor: 'black',
elevation: 0, // for Android
shadowOffset: {
width: 0, height: 0 // for iOS
},
height:Platform.OS==='ios'&& dimen.width>400?80:Platform.OS==='ios'&& dimen.width<400?60:50,
}
}}
initialRouteName={"Home"}
>
<Tab.Screen
name="Home"
component={HomeNavigator}
options={{
tabBarIcon: ({ size, color,focused }) => (
<Entypo name="home" size={30} color={'#969696'}/>
),
headerShown:false,
unmountOnBlur: true,
}}
/>
<Tab.Screen
name="Test"
component={TestNavigator}
options={({ navigation }) => ({
tabBarButton: () => (
<NewListingButton
onPress={() => navigation.navigate(routes.test)}
/>
),
tabBarIcon: ({ size, color }) => (
<FontAwesome
name="life-saver"
size={size}
color={color}
/>
),
headerShown: false,
unmountOnBlur: true,
})
}
/>
<Tab.Screen
name="Help"
component={HelpNavigator}
options={{
tabBarIcon: ({ size, color,focused }) => (
<Ionicons name="help" size={30} color={'#969696'}/>
),
unmountOnBlur: true,
headerShown:false,
}}
/>
</Tab.Navigator>
)
};
export default AppNavigator;
My problem was with react navigation 6 in order to hide the tab navigator on specific screens i had to set the tabBarStyle display to none, which was why the background wasnt changing if the tab had a stack

How to keep track of screen title when bottom navigation is used in react native?

I am making use of bottom navigation in React Native and I have a header which is supposed to show on which screen I am at the moment. I am not sure how to change that text on Tab Bar press. I tried making use of onPress event but seems like it does not work.
Here is my code:
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<View style={styles.header}>
<Text style={styles.headerText}>{headerText}</Text>
<SettingsIcon />
</View>
<View style={styles.main}>
<NavigationContainer >
<Tab.Navigator initialRouteName="Home" tabBarOptions={{
activeTintColor: '#FF9F0A',
inactiveTintColor:'white',
style: {
backgroundColor: '#000000',//color you want to change
}
}}>
<Tab.Screen name="Home" component={Home} options={{
tabBarLabel: 'HOME',
tabBarIcon: ({ color, size }) => (
<HomeTabIcon name="home" color={color} size={size} />
),
}}/>
<Tab.Screen name="Controls" component={Controls} options={{
tabBarLabel: 'CONTROLS',
tabBarIcon: ({ color, size }) => (
<ControlsTabIcon name="controls" color={color} size={size} />
),
}}/>
<Tab.Screen name="Charging" component={Charging} options={{
tabBarLabel: 'CHARGING',
tabBarIcon: ({ color, size }) => (
<ChargingTabIcon name="charging" color={color} size={size} />
),
}}/>
</Tab.Navigator>
</NavigationContainer>
</View>
</View>
you can nest your tab navigator in a screen navigator.
You can change the title of your screen navigator header in function of the name of your active screen.
for more information read this.
import React from 'react';
import { NavigationContainer, getFocusedRouteNameFromRoute } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const RootStack = createStackNavigator();
const Tab = createBottomTabNavigator();
function getHeaderTitle(route : any) {
// If the focused route is not found, we need to assume it's the initial screen
// This can happen during if there hasn't been any navigation inside the screen
// In our case, it's "Feed" as that's the first screen inside the navigator
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
console.log("name route = ", routeName);
switch (routeName) {
case 'Home':
return 'name header tab1';
case 'tab1':
return 'name header tab1';
case 'tab2':
return 'name header tab2';
}
}
const HomeTabs = () => {
return (
<Tab.Navigator>
<Tab.Screen name="tab1" component={Component1} />
<Tab.Screen name="tab2" component={Component2} />
</Tab.Navigator>
);
};
function Root() {
return (
<NavigationContainer>
<RootStack.Navigator>
<RootStack.Screen
name="Home"
component={HomeTabs}
options={({ route }) => ({
headerTitle: getHeaderTitle(route),
})}
/>
</RootStack.Navigator>
</NavigationContainer>
);
};

React Native - Trying to create a drawer with tabs navigators in react navigation without the Drawer Item of the Tabs being rendered

I am trying to create an app where I have a drawer and tabs navigators on each page.
I followed various tutorials and the react navigation docs but could not find a solution.
I have created a drawer navigator and within the <drawer.screen/> component I put my main tab navigator (My Tab navigator has multiple stacks - HomeStack, JoinStack, etc). So far so good however when I navigate to that main tab label in the menu, I return to the same screen I came from last and not to the top of my HomeStack although the "initialRouteName" is set to "Home".
I decided to leave it but remove the label, however it does not remove it completely. It removes the text but still there is a component being rendered there (see Image1 below)
Image1:
DrawerItem still being rendered
DrawerNavigator.js
//imports and stuff
const Drawer = createDrawerNavigator();
function MyDrawer({logout}) {
const nav = useNavigation()
return (
<Drawer.Navigator
initialRouteName={stackNavigationsConsts.HOME_STACK}
drawerPosition="right"
drawerContent={(props) => {
return (
<CustomDrawer nav={nav} drawerProps={props}/>
)
}}
>
<Drawer.Screen name={"בדיקה"} component={MainTabNavigator} options={{drawerLabel: () => null}}/>
<Drawer.Screen name="מאמר" component={Article} />
</Drawer.Navigator>
);
}
MainTabNavigator.js
//imports and stuff
const Tab = createBottomTabNavigator();
export default function MainTabNavigator() {
return (
<Tab.Navigator
initialRouteName={stackNavigationsConsts.HOME_STACK}
tabBarOptions={{
activeTintColor: mainColor.secondaryColor,
inactiveTintColor: mainColor.text,
activeBackgroundColor: mainColor.MainBackgroundColor,
// activeBackgroundColor: mainColor.buttonPress,
inactiveBackgroundColor: mainColor.MainBackgroundColor,
keyboardHidesTabBar: true,
}}
>
<Tab.Screen name={stackNavigationsConsts.HOME_STACK} component={HomeStackScreens}
options={{
tabBarLabel: navigationConsts.HOME,
tabBarIcon: ({ color, size }) => (
homeIcon(color)
),
}}
/>
<Tab.Screen name={stackNavigationsConsts.PROFILE_STACK} component={AnotherStack2Screen} options={{
tabBarLabel: navigationConsts.PROFILE ,
tabBarIcon: ({ color, size }) => (
profileIcon(color)
),
}}/>
<Tab.Screen name={stackNavigationsConsts.JOIN_STACK} component={JoinStackScreens}
options={{
tabBarLabel: navigationConsts.JOIN ,
tabBarIcon: ({ color, size }) => (
JoinIcon(color)
),
}}/>
<Tab.Screen name={stackNavigationsConsts.NOTIFICATIONS_STACK} component={AnotherStackScreen} options={{
tabBarLabel: navigationConsts.NOTIFICATIONS ,
tabBarIcon: ({ color, size }) => (
messagesIcon(color)
),
tabBarBadge: 12
}}/>
<Tab.Screen name={stackNavigationsConsts.ADD_RIDE_STACK} component={AnotherStack1Screen} options={{
tabBarLabel: navigationConsts.ADD_RIDE ,
tabBarIcon: ({ color, size }) => (
addRideIcon(color)
),
}}/>
</Tab.Navigator>
);
}
I found the solution here - How to hide Drawer Item from Drawer #2021 There is a workaround from v5 onward, need to extract the list of routes from the drawer navigation state and filter out the label you don't want to as I understood and it worked perfectly!
Please see below my render function for the custom drawer:
render() {
const {state, ...rest} = this.props.drawerProps
const newState = {...state}
newState.routes = newState.routes.filter((item) => item.name !== "זמני")
return (
<DrawerContentScrollView {...this.props}
>
<View style={styles.avatar}>
<View style={styles.imageContainer}>
<Image style={styles.image} source={require('../../../assets/images/man_smile.png')}/>
<Text style={{...styles.text, fontSize:22, marginTop:5}}>גלעד דהן</Text>
</View>
</View>
<View style={styles.items}>
<DrawerItemList
state={newState}
{...rest}
/>
<DrawerItem label={navigationConsts.MAIN} onPress={() => this.props.nav.navigate(stackNavigationsConsts.HOME_STACK)} labelStyle={styles.text}/>
<DrawerItem label="התנתק" onPress={() => this.props.logout()} labelStyle={styles.text}/>
</View>
</DrawerContentScrollView>

Reversing tab navigator direction to support RTL react navigation

I have been trying to change the tab direction to RTL instead of LTR. Unfortunately, I cannot find out how it works. I used flextDirection in tabBarStyle or style props as "row" or "row-reverse", but when I did so, the whole tab was removed. It only appeared when using column or column-reverse showing at the top or bottom of the screen. Here is my code:
const Tab = createMaterialBottomTabNavigator();
<Tab.Navigator
activeColor={theme.colors.text}
inactiveColor={theme.colors.textAlt}
barStyle={{
backgroundColor: theme.colors.background,
flexDirection: "row-reverse", //In this case an empty tab bar only appears with no screens. Screens only appear when it changes to column
}}
shifting={true}
screenOptions={defaultNavOptions}>
<Tab.Screen
name={userConfig.appTexts.homeScreenName}
component={AppNavigator}
options={{
tabBarIcon: ({ color, size }) => {
return <Ionicons name="ios-home" color={color} size={22} />;
},
}}
initialParams={{ theme, taxonomy }}
/>
<Tab.Screen
name={userConfig.appTexts.settingScreenName}
component={SettingNavigator}
options={{
tabBarIcon: ({ color, size }) => {
return <Ionicons name="ios-settings" color={color} size={22} />;
},
}}
initialParams={{ theme }}
/>
<Tab.Screen
name={userConfig.appTexts.savedScreenName}
component={PostNavigator}
options={{
tabBarIcon: ({ color, size }) => {
return <Ionicons name="md-bookmark" color={color} size={22} />;
},
}}
initialParams={{ saved: true, theme }}
/>
</Tab.Navigator>
I guess you can change style in the tabBarOptions :
<Tab.Navigator
tabBarOptions={{
style: {transform: [{scaleX: -1}]},
}}> {Your}
</Tab.Navigator>
Simply reverse your Tab screens order inside Tab Navigator. So move the last screen to be the first and...