I'm having trouble centering a Text component in React-Native - react-native

I'm creating a custom Header for my Screens and I'm trying to center the route name.
The problem is that there exist another component for my back button and it's making the centering really difficult.
Here is my component;
export default function Header({ navigation }) {
const route = useRoute()
console.log(route)
return (
<SafeAreaView>
<View style={styles.container}>
<TouchableOpacity
style={styles.paddedContainer}
onPress={() => navigation.goBack()}>
<Icon name="left" size={25} color={'black'} />
</TouchableOpacity>
<Text style={styles.routeName}>{route.params.title}</Text>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
width: '100%',
flexDirection: 'row',
backgroundColor: '#fff',
paddingBottom: 10,
elevation: 5,
zIndex: 5,
textAlign: 'center',
},
paddedContainer: {
marginLeft: SIZES.width * 0.05,
marginTop: SIZES.height * 0.02,
},
routeName: {
left: '50%',
fontSize: 22,
fontWeight: 'bold',
color: 'black',
marginTop: SIZES.height * 0.02,
},
})
I've tried textAlign, justifyContent, alignContent and all seems to be not working for my example.

First of all, your container need to have display:flex.
Second, there are two element inside container, so your title cannot be perfectly center.
Considered this, separate the component if you need another behaviour.
Here is the style that you need for center title element, but the <Icon/> will stay on the right. Play with flex-direction to make them in the way you want.
const styles = StyleSheet.create({
container: {
width: '100%',
display:'flex',
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: '#fff',
paddingBottom: 10,
elevation: 5,
zIndex: 5,
textAlign: 'center',
},
paddedContainer: {
marginLeft: SIZES.width * 0.05,
marginTop: SIZES.height * 0.02,
},
routeName: {
left: '50%',
fontSize: 22,
fontWeight: 'bold',
color: 'black',
marginTop: SIZES.height * 0.02,
},
})

Please find below Code:
export default function Header({ navigation }) {
const route = useRoute()
console.log(route)
return (
<SafeAreaView style={{flex:1,justifyContent:'center'}}>
<View style={styles.container}>
<TouchableOpacity
style={styles.paddedContainer}
onPress={() => navigation.goBack()}>
<Icon name="left" size={25} color={'black'} />
</TouchableOpacity>
<Text style={styles.routeName}>{route.params.title}</Text>
</View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
width: '100%',
display:'flex',
flexDirection: 'row',
justifyContent: 'center',
backgroundColor: '#fff',
paddingBottom: 10,
elevation: 5,
zIndex: 5,
textAlign: 'center',
},
paddedContainer: {
marginLeft: SIZES.width * 0.05,
marginTop: SIZES.height * 0.02,
},
routeName: {
left: '50%',
fontSize: 22,
fontWeight: 'bold',
color: 'black',
marginTop: SIZES.height * 0.02,
},
})
Hope it will work!

I came up with an answer since the components of React-Native emit an onLayout prop, We could use it to get the width of an element. Then we can set the initial position of the object as absolute. Then set set the margin to device displays width and then get the width from the onLayout prop like so
<Text onLayout={(e) => setWidth(e.nativeEvent.layout.width)} />
Now that we have the width it's trivial matters to center the text. Just using the style from styleSheet and adding the margin. It would look something like this.
<Text style={{...styles.routeName, marginLeft: SIZES.width / 2 - width / 2}} onLayout={(e) => setWidth(e.nativeEvent.layout.width)} />
This centers it perfectly but with a couple of drawbacks. Since the event is emitted after the component is mounted the component sometimes jumps to place I've yet to come up with a better solution. Hope it helps some lost soul who comes across this.

Related

Underline behind text React Native

I want to make a specific underline behind text in a specific area of the title, like this:
How do I do it with React Native?
This is the situation right now:
<View style={[styles.container, {width}]}>
<Image source={item.image} style={[styles.image, {width, resizeMode: 'contain'}]}/>
<View style={{flex: 0.2}}>
<Text style={[styles.title]}>{item.title}</Text>
</View>
</View>
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
},
image: {
flex: 0.6,
justifyContent:'center'
},
title: {
fontWeight: "600",
fontSize: "26",
color: 'black',
textAlign: 'center',
}
})
I've seen this thread with css Thick underline behind text
You have to position the title absolute to achieve that.
In the following code, I have implemented an example I hope it will help you.
<View style={{marginTop: 20, height: 200, position: 'relative'}}>
<View style={styles.line}></View>
<Text style={styles.title}>Specific Title</Text>
</View>
const styles = StyleSheet.create({
title: {
fontSize: 44,
fontWeight: '500',
lineHeight: 45,
position: 'absolute',
top: 20,
},
line: {
marginTop: 40,
marginLeft: 50,
borderRadius: 10,
height: 15,
width: 100,
backgroundColor: '#3F8ED6',
},
})
result

react-native-awesome-gallery shows only one image without gesture possible

related to : https://github.com/Flair-Dev/react-native-awesome-gallery
I tried many things, but nothing is working.
I made the gesture and reanimation installation as wanted.
what I have :
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Gallery from 'react-native-awesome-gallery';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
const ModalInfos = (props) => {
const [showMenu, setShowMenu] = useState(false)
return (
<View style={style.centeredView}>
<View style={style.modalView}>
<Text>{props.infos.name}</Text>
<Text> lots of infos here</Text>
....
....
....
<Text style={{ fontWeight: 'bold' }}> check menu </Text>
<TouchableOpacity
onPress={() => setShowMenu(true)}
>
<MaterialCommunityIcons name="book-open-variant" size={20} color={'#fff'} />
</TouchableOpacity>
</View>
{
showMenu &&
<View style={style.gallery}>
<GestureHandlerRootView style={{ flex: 1 }}>
<Gallery
data={["http://10.0.2.2:8080/images/menu/" + props.infos.barInfos.photomenu1, "http://10.0.2.2:8080/images/menu/" + props.infos.barInfos.photomenu2]}
onIndexChange={(newIndex) => {
console.log(newIndex);
}}
/>
</GestureHandlerRootView>
</View>
}
</View>
)
}
const style = StyleSheet.create({
centeredView: {
flex: 1,
justifyContent: "center",
alignItems: "center",
marginTop: 22,
},
modalView: {
width: '95%',
margin: 20,
backgroundColor: "white",
borderRadius: 20,
padding: 35,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2
},
shadowOpacity: 0.25,
shadowRadius: 4,
elevation: 5
},
gallery: {
flex: 1,
width: "100%",
height: "100%",
position: 'absolute',
zIndex: 10,
backgroundColor: '#202124e6',
}
});
export default ModalInfos;
With or without the GestureHandlerRootViewits the same result, i can see only the first image, and I can't do anything, no swipe, no zoom, not gesture.
I switched to react-native-image-viewing
works more than perfectly

React Navigation (native): Overflow visible not working on custom header

I'm implementing a custom header for #react-navigation/native-stack. Using React Navigation v6.
One of the elements within the custom header has a native shadow on iOS added (via the style prop). The shadow is a tad bigger than the header and unfortunately, I can't get it to display beyond the boundaries of the header. Of course, I've tried using overflow: visible on basically every component in the tree, but no success. The shadow is clipped off:
Here's my custom header:
function CustomHeader(props: NativeStackHeaderProps) {
const { options, route, navigation } = props;
const insets = useSafeAreaInsets();
const headerHeight = Helpers.getHeaderHeight(insets);
return (
<View style={{
height: headerHeight,
paddingTop: insets.top,
width: '100%',
backgroundColor: Colors.white,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingLeft: 20,
paddingRight: 20,
overflow: 'visible',
}}
>
<View style={{
flex: 1, display: 'flex', alignItems: 'flex-start',
}}
>
{ options.headerLeft ? options.headerLeft({ canGoBack: false }) : (
<TouchableOpacity
onPress={() => route.name === 'Home'
? null
: navigation.reset({ index: 0, routes: [{ name: 'Home' }] })}
>
<Image
style={{ width: Sizing.logo, height: Sizing.logo }}
source={Logo}
/>
</TouchableOpacity>
)}
</View>
<Text style={{
textAlign: 'center', color: Colors.purple,
}}
>
{(options.title || route.name).toUpperCase()}
</Text>
<View style={{
flex: 1, display: 'flex', alignItems: 'flex-end', overflow: 'visible',
}}
>
{ options.headerRight ? options.headerRight({ canGoBack: false }) : null}
</View>
</View>
);
}
The button on the right with the shadow is passed in through the headerRight option and contains this shadow style:
nativeShadow: {
shadowColor: Colors.gray,
shadowOffset: { width: 0, height: 8 },
shadowRadius: Colors.shadows.gray.distance,
shadowOpacity: 0.5,
},
Any idea what I could try next? I don't want to increase the headers' height since this would break the layout elsewhere.
I had a similar problem with the menu. I think i was taking another way and maybe you should try it.
Be sure that all the parent elements has at least the same height than the navmenu's height.
import React, { Component } from 'react';
import { TouchableOpacity, StyleSheet, Image, View, Text } from 'react-native';
const styles = StyleSheet.create({
menuParent: {
marginLeft: 30,
justifyContent: "flex-start",
alignItems: "flex-end",
flex: 1,
height: 250
},
btnContainer: {
width: 40,
height: 40,
elevation: 50,
zIndex: 50,
marginTop: 5
},
image: {
width: 40,
height: 40,
},
menuContainer: {
position: "absolute",
top: 5,
left: -5,
right: -5,
padding: 20,
borderRadius: 10,
elevation: 10,
zIndex: 10,
flex: 1,
height: 230,
backgroundColor: "#fff",
}
});
export default class DefaultHeaderMenu extends Component {
state = {
menuVisible: false
};
constructor(props) {
super(props);
}
render() {
return (
<View style={ styles.menuParent }>
{this.state.menuVisible ? <View style={ styles.menuContainer }></View> : null }
<TouchableOpacity
style={ styles.btnContainer }
onPress={ () => this.setState({ menuVisible: !this.state.menuVisible }) }
>
<Image
source={ require('#/assets/img/menu.png') }
style={ styles.image }
/>
</TouchableOpacity>
</View>
);
}
}
This is the component i used and the passed it to nav as headerRight element.
In my case, the problem was that height: 250 was missing in menuParent.
I hope it helps you and good luck
I had a problem like this once and fixed it by using 'zIndex'. Try setting it as the* max index in your project. Hope it helps

Touchable Opacity messes up width in row container

I'm trying to make these two Card components appear next to each other in a row as shown
here which seems to work when I wrap the component in a View, but appears like this with a bunch of unnecessary space in between when I try it with a TouchableOpacity.
Here is my code for the Card component (currently with TouchableOpacity on and the View wrapper commented out):
export const NavCard = ({
title,
height = 200,
onPress = null,
background = null,
}) => {
return (
<TouchableOpacity
onPress={onPress}
style={[
{ height: height },
background ? styles.cardImage : styles.noImage,
]}
>
{/* <View
style={[
{ height: height },
background ? styles.cardImage : styles.noImage,
]}
> */}
<Image
source={background}
resizeMode="cover"
style={{
height: height,
width: "100%",
borderRadius: 15,
position: "absolute",
top: 0,
right: 0,
}}
/>
<View style={{ padding: 15 }}>
<Text style={styles.title}>{title}</Text>
<Image
style={styles.arrow}
source={require("../assets/arrow-right.png")}
/>
</View>
{/* </View> */}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
cardImage: {
flexGrow: 1,
margin: "2%",
borderRadius: 15,
},
noImage: {
flexGrow: 1,
margin: "2%",
borderRadius: 15,
backgroundColor: "#333436",
},
title: {
fontSize: 24,
color: "#E4E5EA",
fontWeight: "bold",
shadowColor: "#000000",
shadowOffset: { width: 2, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
},
arrow: {
width: 15,
height: 15,
resizeMode: "contain",
position: "absolute",
top: 22,
right: 20,
},
});
Here is the code for the screen:
const HomeScreen = ({ navigation }) => {
return (
<View style={styles.container}>
<View style={styles.rowContainer}>
<NavCard
title="Map"
height={180}
onPress={() => navigation.navigate("Map")}
background={require("../assets/pvdx1.png")}
></NavCard>
<NavCard
title="CAD"
height={180}
background={require("../assets/pvdx1.png")}
onPress={() => navigation.navigate("CADScreen")}
></NavCard>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
paddingTop: 10,
padding: 4,
flex: 1,
},
rowContainer: {
flexDirection: "row",
justifyContent: "space-between",
},
body: {
paddingTop: 10,
fontSize: 16,
color: "#E4E5EA",
},
});
export default HomeScreen
Does anyone know why it's messing up the width of both components if the styles of the View and TouchableOpacity are exactly the same? I'm using React Native.
Edit: Have updated to use Image instead of ImageBackground (code now reflects this), but the result is the same.
I figured out the issue: I was importing TouchableOpacity from react-native-gesture-handler instead of react-native. Changed it and it works just fine.
Probably the problem is in your <ImageBackground /> , Because I replaced that with native <Image /> and it's working. here is how my Image looks, you can compare with your code:
<Image
style={{
height: height,
borderRadius: 15,
position: "absolute",
top: 0,
right: 0,
width: "100%"
}}
resizeMode="cover"
source={{ uri: "https://via.placeholder.com/250x150" }}
/>
Here you can check the working code
for me i had to also set the width on the TouchableOpacity (and the view within)
<TouchableOpacity
style={{flex: 1, width: '100%'}}

How to get Text or Button to register click events on top of an Expo map?

I am trying to have clickable text or a button on top of my Expo maps. My provider is google, and when I try to render text that is clickable on top of my maps it fails. The text shows up, but does not register click events. Any ideas?
Here is some code: (see at the bottom I am loading Return to home. )
<SafeAreaView style={{flex: 1, backgroundColor: HOMESCREEN_BACKGROUND}}>
<View style={styles.container}>
<StatusBar backgroundColor="blue" barStyle="light-content" />
<View style={styles.topBar}>
<Text style={styles.cityText}>
{this.state.city}, {this.state.region}
</Text>
<Text style={styles.usernameText}>
{this.props.user.uid}
</Text>
</View>
<MapView
ref={this.map}
style={styles.map} provider="google" customMapStyle={NIGHT_MAP_STYLE}
initialRegion={{
latitude: this.props.reduxMap.latitude,
longitude: this.props.reduxMap.longitude,
latitudeDelta: this.props.reduxMap.latitudeDelta,
longitudeDelta: this.props.reduxMap.longitudeDelta,
}}
showsScale={true}
showsUserLocation={true}
onChange={(event) => this.handleMapViewChange(event)}
>
{/* {this.renderRelevantQueues()} */}
<Text style={styles.returnToHome} onPress={this.snapMapViewToUser} >Return to home</Text>
</MapView>
<View style={styles.bottomBar}>
<Text style={styles.backText} onPress={() => this.handleBackButtonClicked()}>
Back
</Text>
{this.renderReturnToCurrentLocationSvg()}
</View>
</View>
</SafeAreaView>
In response to some questions about this post.
You can see the text, but you can't click it?
I can see a button or text or really whatever on top of the map. However, the click event is not firing when I physically click on the element.
what does renderReturnToCurrentLocationSvg doing ? does it render view or image that might be infront of your text layer?
This is actually commented out in my code but it renders a series of pins and radiuses around them on the map from a database. Removed the function and the issue still persists.
Can you add styles of current screen?
Yes, here they are:
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: HOMESCREEN_BACKGROUND
},
topBar: {
height: 200,
backgroundColor: HOMESCREEN_BACKGROUND,
},
map: {
flex: 1,
flexDirection: 'column'
},
returnToHome: {
color: 'white',
position: 'absolute',
fontSize: 30,
marginLeft: '30%'
},
createQueue: {
color: 'white',
marginLeft: '50%',
fontSize: 30
},
loadingTxt: {
textAlign: 'center',
fontSize: 18,
marginTop: "100%",
marginLeft: "25%",
width: 200
},
topBar: {
height: '10%',
backgroundColor: HOMESCREEN_BACKGROUND,
justifyContent: 'center',
alignItems: 'center',
borderBottomColor: 'grey',
borderBottomWidth: 1
},
bottomBar: {
height: '10%',
backgroundColor: HOMESCREEN_BACKGROUND,
flexDirection: 'row',
borderTopColor: 'grey',
borderTopWidth: 1,
},
backText: {
color: 'white',
fontSize: 30,
marginLeft: 0
},
cityText: {
color: 'white',
fontSize: 30,
},
usernameText: {
color: 'grey'
}
});