Can't change screen with TabIcon in react native? - react-native

I have an issue when changing screens. If i pressed in the middle of the tabIcon it's not navigating to the screen but if i clicked a little bit left further the screen changes. I think that like there is a sort of a View in absolute above that is preventing me from pressing (Element inspector).
I have my code like this :
<Tab.Navigator
initialRouteName="HomeNavigator"
tabBarOptions={{
showLabel: false,
keyboardHidesTabBar: true,
labelStyle: {
color: 'red'
},
style: {
backgroundColor: '#008D36' ,
paddingTop: 10,
paddingBottom: 10,
minHeight: 65,
color: '#ffffff',
borderTopColor: '#ffffff',
justifyContent: 'flex-start',
alignItems: 'flex-start'
, }
}}
>
<Tab.Screen
name="Home"
component={HomeNavigator}
options={(navigation, route) => ({
title: 'Home',
tabBarIcon: ({ props, focused }) => (
<TouchableWithoutFeedback {...props} >
<View style={{flex: 2, alignItems: 'center'}}>
<Image source={LOGO1} resizeMode="contain" style={[styles.imgSize, focused && { opacity:1 }]} />
<Text style={[styles.label, focused && { opacity:1, textDecorationLine: 'underline' }]}>Home</Text>
</View>
</TouchableWithoutFeedback>
)
})}
/>
<Tab.Screen
name="Home2"
component={HomeNavigator}
options={(navigation, route) => ({
title: 'Home2',
tabBarIcon: ({ props, focused }) => (
<TouchableWithoutFeedback {...props} >
<View style={{flex: 2, alignItems: 'center'}}>
<Image source={LOGO2} resizeMode="contain" style={[styles.imgSize, focused && { opacity:1 }]} />
<Text style={[styles.label, focused && { opacity:1, textDecorationLine: 'underline' }]}>Home2</Text>
</View>
</TouchableWithoutFeedback>
)
})}
/>
</Tab.Navigator>

I think that flex: 2 is out of place at this position of the tabBarIcon. You can use alignItems or justifyContent without telling the view for flex.
<TouchableWithoutFeedback {...props} >
<View style={{alignItems: 'center'}}>
<Image source={LOGO1} resizeMode="contain" style={[styles.imgSize, focused && { opacity:1 }]} />
<Text style={[styles.label, focused && { opacity:1, textDecorationLine: 'underline' }]}>Home</Text>
</View>
</TouchableWithoutFeedback>

Related

navigation.dispatch(DrawerActions.toggleDrawer()) Not opening my Side Drawer,

<Drawer.Screen
name="CheckOutScreen"
component={CheckOutScreen}
options={({ navigation }) => ({
drawerItemStyle: { display: 'none' },
drawerIcon: () => (
<Feather
name="home"
size={20}
color={theme.colors.primary}
style={{ marginRight: 10 }}
/>
),
title: 'CheckOut',
headerStyle: {
borderBottomColor: 'yellow',
height: 90,
backgroundColor: theme.colors.primary,
},
headerTintColor: 'white',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity onPress={() =>navigation.dispatch(DrawerActions.toggleDrawer())}>
<Feather
name="user"
size={30}
color="white"
style={{ marginRight: 10 }}
/>
</TouchableOpacity>
),
headerLeft: () => (
<TouchableOpacity onPress={navigation.goBack}>
<Feather
name="arrow-left"
size={30}
color="white"
style={{ marginLeft: 10 }}
/>
</TouchableOpacity>
),
})}
/>
I have tried navigation.dispatch(DrawerActions.toggleDrawer());
also navigation.dispatch(DrawerActions.openDrawer());
But none of them worked out.
Drawer gets open when i swipe right on the screen it opens but i want to open it on a button click.
kindly help me out

Bottom tab bar : change screen from a component instead of pressing navigator button

I have a bottom tab navigator that works well.
It calls differents screens when pressing the buttons.
What I need to do is to call a screen from a component when I close it by pressing a button.
To add more details, consider that I have two screens (home and messenger).
When I am on the messenger screen, I open a component by pressing a button.
This component takes all the screen. When I close it, I would like to be "redirected" to the home screen directly, not the messenger one.
I work on Expo.
How can I achieve that ?
The navigator code :
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { Text, View, Image } from "react-native";
import News from "../screens/News";
import Explorer from "../screens/Explorer";
import Home from "../screens/Home";
import Scanner from "../screens/Scanner";
import Search from "../screens/Search";
import Publish from "../screens/Publish";
import Write from "../screens/Write";
import Messenger from "../screens/Messenger";
import { RFPercentage } from "react-native-responsive-fontsize";
import { DataContext } from "../components/Context";
import { useState, useContext, useEffect } from "react";
const Tab = createBottomTabNavigator();
export default function Tabs() {
const {
login,
screenName,
openRecharge,
openCollect,
openAskCash,
openTransfer,
openConvert,
menuPage,
notifModal,
} = useContext(DataContext);
console.log(login);
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
tabBarShowLabel: true,
tabBarHideOnKeyboard: true,
tabBarStyle: {
display:
login != 1 ||
openRecharge ||
openCollect ||
openAskCash ||
openConvert ||
openTransfer ||
notifModal ||
menuPage == "index" ||
menuPage == "profile"
? "none"
: "flex",
backgroundColor: "#FFFFFF",
height: "8%",
position: "absolute",
borderTopColor: "transparent",
elevation: login != 1 ? 0 : 20,
},
})}
>
<Tab.Screen
name="Home"
component={Home}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: focused ? "#373945" : "#75787d",
fontFamily: "Jost",
fontWeight: "700",
fontSize: RFPercentage(1.2),
marginTop: -10,
marginBottom: 10,
}}
>
ACCUEIL
</Text>
),
tabBarIcon: ({ focused }) =>
focused ? (
<Image
source={require("../assets/home-active.png")}
style={{ width: 20, height: 20 }}
/>
) : (
<Image
source={require("../assets/home.png")}
style={{ width: 20, height: 20 }}
/>
),
}}
/>
<Tab.Screen
name="Explorer"
component={Explorer}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: focused ? "#373945" : "#75787d",
fontFamily: "Jost",
fontWeight: "700",
fontSize: RFPercentage(1.2),
marginTop: -10,
marginBottom: 10,
}}
>
MARCHÉS
</Text>
),
tabBarIcon: ({ focused }) =>
focused ? (
<Image
source={require("../assets/explorer-active.png")}
style={{ width: 20, height: 20 }}
/>
) : (
<Image
source={require("../assets/explorer.png")}
style={{ width: 20, height: 20 }}
/>
),
}}
/>
{(() => {
switch (screenName) {
case "Home":
return (
<Tab.Screen
name="Scanner"
component={Scanner}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: "#ffffff",
}}
>
SCANNER
</Text>
),
tabBarIcon: ({ focused }) => (
<View
style={{
width: 55,
height: 55,
backgroundColor: "#089baa",
borderRadius: 50,
marginBottom: 30,
justifyContent: "center",
alignItems: "center",
}}
>
<Image
source={require("../assets/scanner-blanc.png")}
style={{ width: 28, height: 28 }}
/>
</View>
),
}}
/>
);
case "Explorer":
return (
<Tab.Screen
name="Search"
component={Search}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: "#ffffff",
}}
>
SEARCH
</Text>
),
tabBarIcon: ({ focused }) => (
<View
style={{
width: 55,
height: 55,
backgroundColor: "#089baa",
borderRadius: 50,
marginBottom: 30,
justifyContent: "center",
alignItems: "center",
}}
>
<Image
source={require("../assets/glass-white.png")}
style={{ width: 28, height: 28 }}
/>
</View>
),
}}
/>
);
case "News":
return (
<Tab.Screen
name="Publish"
component={Publish}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: "#ffffff",
}}
>
PUBLISH
</Text>
),
tabBarIcon: ({ focused }) => (
<View
style={{
width: 55,
height: 55,
backgroundColor: "#089baa",
borderRadius: 50,
marginBottom: 30,
justifyContent: "center",
alignItems: "center",
}}
>
<Image
source={require("../assets/add.png")}
style={{ width: 28, height: 28 }}
/>
</View>
),
}}
/>
);
case "Messenger":
return (
<Tab.Screen
name="Write"
component={Write}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: "#ffffff",
}}
>
MESSENGER
</Text>
),
tabBarIcon: ({ focused }) => (
<View
style={{
width: 55,
height: 55,
backgroundColor: "#089baa",
borderRadius: 50,
marginBottom: 30,
justifyContent: "center",
alignItems: "center",
}}
>
<Image
source={require("../assets/pencil.png")}
style={{ width: 28, height: 28 }}
/>
</View>
),
}}
/>
);
default:
return null;
}
})()}
<Tab.Screen
name="News"
component={News}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: focused ? "#373945" : "#75787d",
fontFamily: "Jost",
fontWeight: "700",
fontSize: RFPercentage(1.2),
marginTop: -10,
marginBottom: 10,
}}
>
FIL D'ACTUS
</Text>
),
tabBarIcon: ({ focused }) =>
focused ? (
<Image
source={require("../assets/news-active.png")}
style={{ width: 20, height: 20 }}
/>
) : (
<Image
source={require("../assets/news.png")}
style={{ width: 20, height: 20 }}
/>
),
}}
/>
<Tab.Screen
name="Messenger"
component={Messenger}
options={{
unmountOnBlur: true,
tabBarLabel: ({ focused }) => (
<Text
style={{
color: focused ? "#373945" : "#75787d",
fontFamily: "Jost",
fontWeight: "700",
fontSize: RFPercentage(1.2),
marginTop: -10,
marginBottom: 10,
}}
>
MESSAGES
</Text>
),
tabBarIcon: ({ focused }) =>
focused ? (
<Image
source={require("../assets/messagerie-active.png")}
style={{ width: 20, height: 20 }}
/>
) : (
<Image
source={require("../assets/messagerie.png")}
style={{ width: 20, height: 20 }}
/>
),
}}
/>
</Tab.Navigator>
);
}
I faced the same issue before, this is how I solved it:
Created a global flag that changes to a specific value (example: 1) when I go to the target screen, in your case the Component which takes the whole screen
I perform a check in the Messenger screen in a useFocusEffect hook that if the flag is 1 redirect to the home screen and change the flag to 0
This way I can visit the Messenger screen again cause the value will be 0 but once I open the component it will change to 1 and once I press back I will be redirected to Home Screen
The useFocusEffect part:
import { useFocusEffect } from "#react-navigation/native";
export default function ConfirmOrder(){
useFocusEffect(
React.useCallback(() => {
if (cart.length == 0) {
navigation.navigate("Home");
}
})
);
return(
<Text>Confirm Order Page</Text>
)
}

Globally accessible variable to control Bottom Tab Navigator in React Native app

I am using createBottomTabNavigator from #react-navigation/bottom-tabs to make a nice bottom navigation for my application. I have added some custom notification indicators for the tab bar icons, that illustrates "news" inside the specific stacks. These icons (dots) should be visible if a variable is true and otherwise the dot shoul be hidden. The variable will be depending on the content of the stack so if there are new messages in the MessagesStack, the unread_messages = true and so on...
I am looking for a solution where I can access these variables in my TabStack from the specific stacks, so when I call the API in the MessagesStack and there is a new message, I can update unread_messages in the TabStack and show the dot.
I have pasted the full code for my TabStack in the App.js-file below:
TabStack = () => {
StatusBar.setBarStyle("dark-content");
const insets = useSafeAreaInsets();
return (
<View style={main_Styles.mainBackground}>
<View style={{ flex: 1, paddingBottom: insets.bottom,}} >
<View style={{ flex: 1}} >
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconOpacity = (focused) ? 1.0 : 0.2;
let iconSize = 28;
if (route.name === 'HomeStack') {
return (
<View style={{alignItems: 'center'}}>
<Icon name='home_white' size={iconSize} opacity={iconOpacity}/>
</View>
);
}
if (route.name === 'MessagesStack') {
return (
<View style={{alignItems: 'center'}}>
<Icon name='messages_white' size={30} opacity={iconOpacity}/>
{((unread_messages ?? 0) > 0) && <View style={{backgroundColor: Main.colours.yellow_medium, width: 6, height: 6, borderRadius: 3}} />}
</View>
);
}
if (route.name === 'GroupsStack') {
return (
<View style={{alignItems: 'center'}}>
<Icon name='group_white' size={iconSize} opacity={iconOpacity}/>
</View>
);
}
if (route.name === 'NotificationsStack') {
return (
<View style={{alignItems: 'center'}}>
<Icon name='notifications_white' size={iconSize} opacity={iconOpacity}/>
{((unread_notifications ?? 0) > 0) && <View style={{backgroundColor: Main.colours.yellow_medium, width: 6, height: 6, borderRadius: 3, marginTop: 1}} />}
</View>
);
}
if (route.name === 'MyProfileStack') {
return (
<View style={{alignItems: 'center'}}>
<View style={{marginTop: 3, flexDirection: 'row', width: 24, height: 24, borderRadius: 12, opacity: iconOpacity, borderColor: Main.colours.red_light, borderWidth: 0, overflow: 'hidden'}}>
<ProfileImage style={{flex: 1, backgroundColor: 'red'}} image_url={this.state.profile_image_url} initials={this.state.initials}/>
</View>
{(pending_friend_requests > 0 || pending_event_applications > 0 || pending_group_applications > 0 ) && <View style={{backgroundColor: Main.colours.yellow_medium, width: 6, height: 6, borderRadius: 3, marginTop: 2}} />}
</View>
)
}
},
headerShown: false,
lazy: false,
optimizationsEnabled: false,
animationEnabled: true,
activeTintColor: Main.colours.white,
showIcon: true,
swipeEnabled: Platform.OS === 'ios' ? true : false,
tabBarShowLabel: false,
scrollEnabled: true,
tabBarStyle: {
backgroundColor: Main.colours.red_medium,
borderTopWidth: 0,
shadowOpacity: 0,
elevation: 0,
paddingTop: 0,
paddingBottom: 0,
height: 49,
},
tabBarIndicatorStyle: {
backgroundColor: 'transparent',
},
})}>
<Tab.Screen name="HomeStack" component={HomeStack} />
<Tab.Screen name="MessagesStack" component={MessagesStack}/>
<Tab.Screen name="GroupsStack" component={GroupsStack}/>
<Tab.Screen name="NotificationsStack" component={NotificationsStack} />
<Tab.Screen name="MyProfileStack" component={MyProfileStack} />
</Tab.Navigator>
</View>
</View>
</View>
);
}
Here, there are three solutions for updating icons in the tab
First, create Tab Icon as a functional component and then you can bind
the API with redux, and once redux is updated, it will update the tab
icon component, use redux context
Use global app context.
for eg. create one state in the main app context and update that context
once your API call is done so it will automatically update your tab
component
fire local notification and update date the tab component(not
recommended)

how to add left border color to active drawer menu?

i am working on a react native 0.62 in which i have implemented drawer navigator. As per the documentation, i have properly added activeBackgroundColor, activeTintColor etc but as per the company's requirement, when the menu is active i wanted to add borderLeftColor also with activeBackgroundColor. i have tried using style property but it didn't work for me.
Mock Up:
My Current UI:
MainNavigator.js
<Drawer.Navigator initialRouteName="Dashboard" drawerContent={(props) => <DrawerContent {...props} />} hideStatusBar={false} focused={true} labelStyle={{ fontSize: 14, fontFamily: 'OpenSans-SemiBold' }} drawerContentOptions={{ activeBackgroundColor: "#F1F1F1", activeTintColor: "#000000", inactiveTintColor: "#818181",itemStyle: { marginLeft:0, paddingHorizontal: 10, width:'100%', borderRadius: 0}}} indicatorStyle={{
borderBottomWidth: 2,
borderBottomColor: 'red',
}}
>
<Drawer.Screen name="Dashboard" component={DashboardStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/dashboard.png')} style={{ height: 17.78, width: 16}} resizeMode="contain"/>
),
}}
/>
<Drawer.Screen name="My Profile" component={MyProfileStackScreen} options={{
drawerIcon: ({ focused, size }) => (
<Image source={require('../assets/images/profile.png')} style={{ height: 16, width: 16 }} resizeMode="contain"/>
),
}} />
</Drawer.Navigator >
DrawerContent.js
<DrawerContentScrollView {...props} >
<DrawerItemList {...props}
/>
<DrawerItem
labelStyle={{ fontSize: 14, fontFamily: 'OpenSans-SemiBold' }} activeBackgroundColor= "#F1F1F1" activeTintColor="#000000" inactiveTintColor= "#818181"
label="Logout"
icon={({ focused, color, size })=>{
return(
<Image source={require('../assets/images/logout.png')} style={{ height: 14.36, width: 14.36 }} resizeMode="contain"/>)
}}
onPress={() => {resetData(); props.dispatch({type:'AUTH_FAILURE', payload: ''}); props.dispatch(onClear())} }
/>
</DrawerContentScrollView>
Thank you in advance.
As of now the drawer navigation 5 doesnt support an active style. But you can wrap the icon in a View and add a border to it which would give give you something similar.
Not the perfect solution but will get you close to the expected output you have provided.
<Drawer.Screen
name="My Profile"
component={MyProfileStackScreen}
options={{
drawerIcon: ({ focused, size }) => (
<View
style={
focused
? {
borderLeftColor: 'red',
borderLeftWidth: 2,
paddingLeft: 5,
}
: null
}>
<Image
source={require('../assets/images/profile.png')}
style={{ height: 17.78, width: 16 }}
resizeMode="contain"
/>
</View>
),
}}
/>
I know I am too late, but may be helpful in future for someone
I achieved the design by doing like below,
here is my CustomDrawerComponent
I used props -> state object to identify the active route name
Then apply conditional styles to <DrawerItem style={{}}/>
import { createDrawerNavigator } from '#react-navigation/drawer';
import {DrawerItem,DrawerContentScrollView} from '#react-navigation/drawer';
const CustomDrawerContent = props => {
const {state} = props;
const {routes, index} = state;
//here we get the active route name
const focusedRoute = routes[index].name;
return (
<View style={{flex: 1}}>
<ProfileHeader />
<DrawerContentScrollView
{...props}
contentContainerStyle={{paddingTop: 0, flex: 1}}>
<DrawerItem
{...props}
label="Screen1"
style={
focusedRoute === 'Screen1' ? styles.itemActive : styles.itemInactive
}
labelStyle={{}}
icon={({}) => <Icon />}
onPress={() => {
props.navigation.navigate('Screen1');
}}
/>
<DrawerItem
{...props}
label="Screen1"
style={
focusedRoute === 'Screen2' ? styles.itemActive : styles.itemInactive
}
labelStyle={{}}
icon={({}) => <Icon />}
onPress={() => {
props.navigation.navigate('Screen2');
}}
/>
<DrawerItem
{...props}
label="Logout"
style={styles.itemInactive}
labelStyle={{}}
icon={({}) => <Icon />}
/>
</DrawerContentScrollView>
</View>
);
};
Here is my Root Drawer.Navigator setup
<Drawer.Navigator
drawerContent={props => <CustomDrawerContent {...props} />}>
// Drawer Screens
</Drawer.Navigator>

How can i update or pass badge count from component to Tab.Navigator. React-native

I want to update or show the badge count in bottom tabs, how can I pass count from API in Component to Tab.Navigator and display the count in the bottom tabs.
sample code
`import Channels from '#channels';
import Documents from '#documents';
render(){
return(
<Tab.Navigator
initialRouteName={this.state.selectedTab}
tabBarOptions={{
activeTintColor: '#248E42',
labelStyle:{fontSize:width/35'}
}}
>
<Tab.Screen
name="Channels"
component={Channels}
options={{
tabBarLabel: 'Channels',
tabBarIcon: ({ focused,badgeCount }) => (
<View>
<Image source={Images.iconChannel} style={{ height: 24, width: 24 }} />
{badgeCount < 0 &&
(
<View style={styles.badge}>
<Text style={{ color: 'white', fontSize: 10, fontWeight: 'bold' }}>{badgeCount}</Text>
</View>
)
}
</View>
)
}}
/>
<Tab.Screen
name="Documents"
component={Documents}
options={{
tabBarLabel: 'Documents',
tabBarIcon: ({ focused }) => (
<Image source={Images.iconDocuments} style={{ height: 24, width: 24 }} />
),
}}
/>
</Tab.Navigator>)}
`
Your Environment
`#react-navigation/native - ^5.1.7
#react-navigation/bottom-tabs - ^5.2.8
react-native-screens - ^2.7.0
react-native - 0.62.2`
I was also having this problem for my notifications badge in bottom tabs. So, I made tabbarIcon a separate component and connected it with app state.
class BottomTabIcon extends Component {
render() {
const { color, notifications: { data: notifications } } = this.props;
const badgeCount = _.filter(notifications, notification => !notification.is_read).length;
return (
<View style={{ width: 24, height: 24, margin: 5 }}>
<Image source={images.bottom_notification} style={{ tintColor: color }} />
<View
style={{
position: 'absolute',
right: -6,
top: -3,
backgroundColor: 'red',
padding: 3,
borderRadius: 6,
// width: 12,
// height: 12,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Text style={{ color: 'white', fontSize: 10, fontWeight: 'bold' }}>
{badgeCount}
</Text>
</View>
</View>
);
}
}
const mapStateToProps = state => ({
notifications: state.notifications
})
export default connect(mapStateToProps)(BottomTabIcon);
Then Imported the component to use as tabbarIcon.
<Tab.Navigator
initialRouteName={this.state.selectedTab}
tabBarOptions={{
activeTintColor: '#248E42',
labelStyle:{fontSize:width/35'}
}}
>
<Tab.Screen
name="Channels"
component={Channels}
options={{
tabBarLabel: 'Channels',
tabBarIcon: (props) => <BottomTabIcon {...props} />
)
}}
/>
</Tab.Navigator>
This solution worked for me, I hope this solves your problem also
I connected the whole TabNavigator and passed the needed properties.