tab bar icons not perfectly centered when screen changes - react-native

I have a issue with my costume tab bar and that is when I select other screens icons lose their position a little bit and shift to for example right. for better understanding consider these two images:
you can see this compass icon is not centered perfectly:
but when I tap on screen this become perfectly centered:
here's my styles on custom bottom tab bar :
const TabComponent = ({label, accessibilityState, onPress}) => {
const focused = accessibilityState.selected;
const icon = focused ? ICONS[`${label}Active`] : ICONS[label]
return (
<TouchableWithoutFeedback onPress={onPress}>
<View focused={focused} style={[styles.container, focused ? styles.focused : null]}>
<Image style={styles.icon} source={icon} />
</View>
</TouchableWithoutFeedback>
)
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 20
},
focused: {
backgroundColor: '#3170FF',
borderRadius: 25,
width: 46,
height: 75,
position: 'relative',
bottom: 20
},
icon: {
width: 24,
height: 24,
}
})
and below is my styles on bottom tab itself:
<Tab.Navigator tabBarOptions={{
style: {
borderTopLeftRadius: 23,
borderTopRightRadius: 23,
height: 64,
alignItems: 'center',
}
}} >
<Tab.Screen name="Home" component={Home} options={{
tabBarButton: (props) => <TabComponent label='home' {...props} />,
}}/>
...
how can I fix this issue? any help would be great.

Related

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>
)
}

Blank screen when I put Drawer Navigator as the main navigator in index.tsx

If I put Drawer Navigator in index.tsx I only get a blank screen.
Tried wrapping NavigationContainer in index.tsx in a View with flex: 1 but it didn't work.
Also I don't have alignItems: 'center' in App.tsx to remove as this answer suggests. (https://stackoverflow.com/a/62647417/13117660)
index.tsx:
const Drawer = createDrawerNavigator();
export const store = configureStore({});
export default () => {
return (
<Provider store={store}>
<DatabaseConnectionProvider>
<NavigationContainer>
<Drawer.Navigator drawerContent={(props: any) => <CustomDrawerContent {...props} />} screenOptions={{
headerTitle: () => <Image source={require('../assets/logo_navbar.jpg')} style={{
width: 120,
height: 40,
alignContent: 'center'
}} />,
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: Colors.RED,
}
}} >
<Drawer.Screen name='Home' component={HomeScreen}></Drawer.Screen>
</Drawer.Navigator>
</NavigationContainer>
</DatabaseConnectionProvider>
</Provider>
);
};
CustomDrawerContent.tsx:
const CustomDrawerContent: FC = (props: any) => {
return (
<View style={{ flex: 1, backgroundColor: Colors.RED }}>
<DrawerContentScrollView {...props} style={styles.drawerContent2}>
<View style={styles.drawerContent}>
<Drawer.Section style={styles.drawerSection}>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Home"}
onPress={() => {
props.navigation.navigate("Home");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"About us"}
onPress={() => {
props.navigation.navigate("AboutUs");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Impresum"}
onPress={() => {
props.navigation.navigate("Impresum");
}}
/>
<DrawerItem
labelStyle={{ color: Colors.WHITE, fontSize: 20, marginLeft: 20 }}
label={"Contact"}
onPress={() => {
props.navigation.navigate("Contact");
}}
/>
</Drawer.Section>
</View>
</DrawerContentScrollView>
</View>
);
};
export default CustomDrawerContent;
const styles = StyleSheet.create({
drawerContent: {
flex: 1,
backgroundColor: Colors.RED,
},
drawerContent2: {
flex: 1,
},
labels: {
fontSize: 30,
},
userInfoSection: {
paddingLeft: 20,
},
title: {
fontSize: 16,
marginTop: 3,
fontWeight: "bold",
},
caption: {
fontSize: 14,
lineHeight: 14,
},
row: {
marginTop: 20,
flexDirection: "row",
alignItems: "center",
},
section: {
flexDirection: "row",
alignItems: "center",
marginRight: 15,
},
paragraph: {
fontWeight: "bold",
marginRight: 3,
},
drawerSection: {
marginTop: 15,
fontWeight: "bold",
backgroundColor: Colors.RED,
},
});
Edit:
Also when I use the Drawer Navigator like this in index.tsx it also shows just blank screen
return (
<Provider store={store}>
<DatabaseConnectionProvider>
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name='Home' component={HomeScreen}></Drawer.Screen>
<Drawer.Screen name='About' component={AboutUsScreen}></Drawer.Screen>
</Drawer.Navigator>
</NavigationContainer>
</DatabaseConnectionProvider>
</Provider>
);
When I put a breakpoint on the return of HomeScreen component it gets hit only the first time the app loads, but nothing renders on screen. If I reload the app than the HomeScreen breakpoint is not hit. I have to stop the server and run the app again every time. But the blank screen is always there.
If I change the Drawer Navigatior to Stack Navigatior than everything works. But I need the Drawer Navigator to be the main navigator.
I am using expo, #react-navigation/drawer 6.3.0 and #react-navigation/native 6.0.8
So I found out what wasn't working. My debbuger was on and it was causing problems as explained here: https://github.com/react-navigation/react-navigation/issues/10253
I am debugging in VS code, not in Chrome Debugger but still when I turn off debugging it works. If the debugger is on you have to put
useLegacyImplementation={false}
as a prop in Drawer.Navigator. It is little laggy but works.
I am using react-native-reanimated 2.4.0

react-native-tab-view width not getting 100% with two tab bars

When two custom tab bars are used in react-native-tab-view, the width is not getting 100%, ie; in the image shown, there is an extra space after the 2nd tab(On Riding). When more than 2 tabs are there, it works fine. What I expect is to get 50% width for both tab bars without any red space. Screenshot of tab view
---------- TabView
<TabView
navigationState={{index, routes}}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{width: layout.width}}
renderTabBar={(props) => (
<TabBar
{...props}
scrollEnabled
style={{
borderRadius: 20,
elevation: 0,
backgroundColor: 'red',
}}
renderIndicator={() => null}
onTabLongPress={(scene) => {
const {route} = scene;
props.jumpTo(route.key);
}}
renderTabBarItem={({route, focused, color, scene}) => {
return (
<Pressable
onPress={() => {
props.jumpTo(route.key);
}}
style={[
route.key === props.navigationState.routes[index].key
? Styles.tabBarActive
: Styles.tabBarInActive,
Styles.tabBar,
]}>
<Text
style={[
route.key === props.navigationState.routes[index].key
? Styles.activeTabText
: Styles.inActiveTabText,
]}>
{route.title}
</Text>
</Pressable>
);
}}
/>
---------- Styles.js
tabBar: {
paddingHorizontal: '5%',
paddingVertical: '2%',
alignItems: 'center',
flex: 1,
},
tabBarActive: {
borderRadius: RFValue(25),
backgroundColor: Colors.AppPrimaryThemeColor,
justifyContent: 'center',
},
tabBarInActive: {
backgroundColor: 'green',
justifyContent: 'center',
},
activeTabText: {
color: Colors.AppMainColor,
fontFamily: Fonts.FONT_BOLD,
fontSize: RFPercentage(1.8),
},
inActiveTabText: {
color: Colors.AppPrimaryThemeColor,
fontFamily: Fonts.FONT_MEDIUM,
fontSize: RFPercentage(1.6),
},
Do you really need to scroll between the tabs?
if not, try deleting "ScrollEnabled".
renderTabBar={(props) => (
<TabBar
{...props}
style={{
borderRadius: 20,
elevation: 0,
backgroundColor: 'red',
}}

Is there any way I can place the TabBar over my header

I had an idea in my design but making it proved harder than I thought. The design is as follows:
The tabbar here is made with 'createMaterialTopTabNavigator'.
My idea was that the gradient part will be the header so it won't move when navigating from INFO to PHOTOS. I am able the screen the way it looks in the picture above except for the TopTabNavigator overlapping the header. It looks as follows:
I do understand why this is happening since the topTabNavigator is inside of the stackNavigator and thus the header will always be on top. The thing is that I want the gradient part to stay in place when swiping left or right through the topTabNavigator.
The profile Stack Screen:
const ProfileStackScreen = ({ navigation }) => {
return (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Profile" component={ProfileTabScreen} options={{
header: () => ( <ProfileHeader onPress={() => {navigation.openDrawer()}} />)
}} />
</ProfileStack.Navigator>
);
};
The header:
const ProfileHeader = (props) => {
return (
<LinearGradient colors={[color1, color2]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={{
width: '100%,
alignItems: 'center',
paddingVertical: 50,
}}>
<View style={{width: '100%}}>
<View style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
width: '100%',
}}>
<TouchableOpacity><Icon /></TouchableOpacity>
<TouchableOpacity><Text>Edit profile</Text></TouchableOpacity>
</View>
</View>
<Image style={{height: 115, width: 115, borderRadius: 100}} source={require('../path/img.jpg')} />
</LinearGradient>
);
}

How to add clients logo on bottom Tab Bar

i am creating an android based mobile app using react native. I want to add a logo on my bottom tab. The tab bar also contains 4 tabs. I need to display a logo on left corner of my device. Logo is not a clickable one, just an image.
For React Navigation 5.
You can show custom BottomTabBar with tabBar options.
Define Tab stack
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Tab.Navigator tabBar={props => <BottomTabBar {...props} />}>
<Tab.Screen name="Tab1" component={Screen3} />
<Tab.Screen name="Tab2" component={Screen2} />
<Tab.Screen name="Tab3" component={Screen2} />
<Tab.Screen name="Tab4" component={Screen2} />
</Tab.Navigator>
);
}
Create Custom BottomTabBar
import React from 'react';
import {View,Text,Image,Platform,TouchableOpacity,StyleSheet} from 'react-native';
const BottomTabBar = ({state, navigation, ...props}) => {
const {routes = [], index: activeIndex} = state;
return (
<View style={styles.container}>
<Image
source={require('../../assets/logo.png')}
style={styles.imageIcon}
/>
<View style={styles.tabContainer}>
{routes.map((it, index) => {
return (
<TouchableOpacity
onPress={() => {
navigation.jumpTo(it.name);
}}
style={[
styles.tabButton,
{
borderBottomWidth: activeIndex === index ? 1 : 0,
},
]}>
<Text>{it.name}</Text>
</TouchableOpacity>
);
})}
</View>
</View>
);
};
export default BottomTabBar;
const styles = StyleSheet.create({
tabButton: {
flex: 1,
height: 50,
justifyContent: 'center',
alignItems: 'center',
borderBottomColor: 'red',
},
tabContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-evenly',
},
imageIcon: {
width: 50,
height: 50,
resizeMode: 'contain',
},
container: {
width: '100%',
flexDirection: 'row',
alignItems: 'center',
height: 64,
paddingHorizontal: 16,
backgroundColor: 'white',
paddingBottom: Platform.OS === 'ios' ? 15 : 0,
},
});
ScreenShot