I'm practicing some react native code and I'm currently stuck with an overlapping problem.I've got this custom component:
const MobileNumberInput = ({ value, valueChanged, placeholder, valid }) => {
return (
<View style={{ flexDirection: "row", flex: 1 }}>
<TextInput
editable={false}
style={[styles.textBoxCountryCode, !valid ? styles.invalidTextBox : ""]}
value="+90"
/>
<TextInput
style={[styles.textBoxPhoneNumber, !valid ? styles.invalidTextBox : ""]}
placeholder={placeholder}
value={value}
onChangeText={valueChanged}
keyboardType="number-pad"
maxLength={9}
/>
</View>
);
};
export default MobileNumberInput;
onst styles = StyleSheet.create({
container: {
flexDirection: "row",
flex: 1,
height: 40
},
textBoxCountryCode: {
height: 40,
borderColor: "gray",
borderTopWidth: 1,
borderBottomWidth: 1,
borderLeftWidth: 1,
borderTopLeftRadius: 5,
borderBottomLeftRadius: 5,
padding: 5,
flex: 2
},
textBoxPhoneNumber: {
height: 40,
borderColor: "gray",
borderTopWidth: 1,
borderBottomWidth: 1,
borderRightWidth: 1,
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
paddingTop: 5,
paddingRight: 5,
paddingBottom: 5,
marginBottom: 10,
flex: 11
},
invalidTextBox: {
borderColor: "red"
}
});
And here, I'm using that component:
renderError = id => {
const { validationErrors } = this.state;
if (validationErrors[id]) {
return (
<Text style={styles.validationError}>{validationErrors[id][0]}</Text>
);
}
return null;
};
render() {
const keyboardVerticalOffset = Platform.OS === "ios" ? 40 : 0;
return (
<View style={styles.container}>
<KeyboardAvoidingView
behavior="padding"
style={styles.textBoxContainerContainer}
keyboardVerticalOffset={keyboardVerticalOffset}
>
<View style={styles.textBoxContainer}>
<TextBoxMA
style={styles.textBox}
placeholder={Strings.USERNAME_PLACEHOLDER}
value={this.state.userName}
onChangeText={this.handleTextChange("userName")}
valid={this.fieldIsValid("userName")}
/>
{this.renderError("userName")}
</View>
<View style={styles.textBoxContainer}>
<MobileNumberInput
value={this.state.originalMobile}
valueChanged={this.handleTextChange("originalMobile")}
placeholder={Strings.ORIGINAL_MOBILE_PHONE_PLACEHOLDER}
valid={this.fieldIsValid("originalMobile")}
/>
{this.renderError("originalMobile")}
</View>
</KeyboardAvoidingView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20
},
textBoxContainerContainer: {
flex: 4,
justifyContent: "flex-end"
},
textBoxContainer: {
height: 40,
marginBottom: 20
},
validationError: {
fontSize: 12,
color: "red",
fontWeight: "bold",
paddingTop: 3
}
});
The {this.renderError("originalMobile")} portion is responsible for rendering an error text.
I put it there to get a small red warning just under the text box. But the warning stretches to inside mobile number component while the username warning is displayed correctly. Here's the output:
I've tried wrapping the error Text inside another View, didn't help. Wrapped the container View inside the MobileNumberInput component into yet another View with explicit flex-direction ="column". It didn't help either. I even changed the flex-direction of that container View to "column" just to see what would happen. But it didn't have any effect either. As you can see from the screenshot the problem is with the MobileNumberInput component part. The error text for the Username textinput is ok.
And if you wonder what the TextBoxMA looks like, it's just a TextInput component with predefined properties. Here it is:
export const TextBoxMA = props => {
return (
<TextInput
secureTextEntry={props.isPassword || false}
{...props}
style={[
props.style,
styles.textBox,
Platform.OS === "ios" ? styles.textBoxIos : styles.textBoxAndroid,
!props.valid ? styles.invalidTextBox : ""
]}
/>
);
};
const styles = StyleSheet.create({
textBox: {
height: 40,
borderColor: "gray",
borderWidth: 1,
borderRadius: 5,
padding: 5
},
invalidTextBox: {
borderColor: "red"
}
});
You've set your input height to 40, but then the View with style styles.textBoxContainer which wraps both input and error text, is also set to 40 height, squishing MobileNumberInput in half. You can't visually notice this effect because TextInputs inside MobileNumberInput go outside of the parent
To handle cases like this you need to create one container to wrap TextInputs and another container to wrap the first container and the error text. It's best to not set explicit height for containers but rather set their paddings to get desired size
Related
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
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%'}}
I ma new to React native development. In my application I have login screen. In laogin screen I have two text inputs along with images. I have taken image and Text input in one view and given flex direction as row. And I have given text input as alignSelf stretch. So here my issue is I need full length of text input along with image. But if I removed flex direction then will get full length of the screen. The following is the code
import React, {Component} from 'react';
import {StyleSheet, Text, View,TextInput,TouchableOpacity,StatusBar,Image} from 'react-native';
export default class App extends Component {
static navigationOptions = {
title: "Welcome",
header: null,
}
render() {
// const { navigate } = this.props.navigation
return (
<View style={styles.container}>
<StatusBar
barStyle="light-content"
backgroundColor="#2f5597"
/>
<Image source={require('../../app/images/ic_accountlist.png')} style={styles.logo}/>
<View style={styles.loginView}>
<View >
<Image source={require('../../app/images/ic_userid.png')} />
<TextInput placeholder="Acct No/User Id" style={styles.textInput} underlineColorAndroid={'rgb(0,0,0)'}></TextInput>
</View>
<View style={styles.user}>
<Image source={require('../../app/images/ic_password.png')} />
<TextInput placeholder="Password" style={styles.textInput} underlineColorAndroid={'rgb(0,0,0)'}></TextInput>
</View>
</View>
<TouchableOpacity style={styles.btnLogin} onPress={this.login}><Text style={{color: 'white'}}>Log In</Text></TouchableOpacity>
</View>
);
}
login=()=>{
// alert("testing......");
this.props.navigation.navigate('Profile');
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCF0',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color:'#FF0000',
},
textInput: {
alignSelf: 'stretch',
padding: 10,
marginLeft: 5,
marginRight:0,
},
btnLogin:{
borderRadius: 10,
backgroundColor: '#2f5597',
padding: 10,
marginTop: 20,
paddingLeft: 50,
paddingRight: 50,
alignSelf: 'center',
},
user:{
flexDirection: 'row',
},
logo :{
marginTop: 100,
alignSelf: 'center',
},
loginView:{
marginTop: 60,
}
});
And the following is the output I am getting
And here I need the text input full along with image. Like the below
There is no need to use the alignSelf property. Instead, you have to use flex: 1:
textInput: {
flex: 1,
padding: 10,
marginLeft: 5,
marginRight: 0,
},
flex: 1 is a shorthand notation, so you could also use explicitly flexGrow: 1 to achieve the same effect:
textInput: {
flexGrow: 1,
padding: 10,
marginLeft: 5,
marginRight: 0,
},
You can read more about flex on MDN.
Result (ignore the different icon):
You need to add flex: 1 to textInput style
textInput: {
alignSelf: 'stretch',
padding: 10,
marginLeft: 5,
marginRight:0,
flex: 1
}
I am trying to create a small tooltip in react native. For the most part it works. But want to use alignself, to auto size the view that contains text. It works with position relative, but that pushes the content away. Position absolute puts all the text inside the tooltip below each other and the tooltip is very narrow.
I have seen some modules for react native tooltip, but those use actions, and I just need to use text.
Anybody an idea on how to get this working?
The is what I have:
constructor(props) {
super(props);
this.state = {
visible: false,
};
}
toggleStatus() {
this.setState({visible: true});
setTimeout(() => {
this.setState({visible: false});
}, 3000);
}
toolTip = () => {
return (
<View style={styles.containerMain}>
{renderIf(this.state.visible,
<View style={styles.tooltipWrapper}>
<Text style={styles.tooltipStyle}>{this.props.tooltipText}</Text>
</View>
)}
<View style={styles.questionMarkWrapper}>
<TouchableOpacity disabled={this.state.visible} onPress={()=>this.toggleStatus()}>
<Text style={styles.questionMark}>?</Text>
</TouchableOpacity>
</View>
</View>
)
}
render() {
return (
<View style={styles.containerStyle}>
{this.toolTip()}
</View>
)
}
And styling I use:
containerMain: {
alignItems: 'flex-end',
},
tooltipWrapper: {
position: 'absolute',
top: 0,
right: 10,
padding: 5,
borderRadius: 4,
backgroundColor: 'rgba(0,0,0,0.6)',
},
tooltipStyle: {
fontSize: 11,
color: 'white',
},
questionMarkWrapper: {
justifyContent: 'center',
flexDirection: 'row',
marginRight: 10,
},
questionMark: {
fontSize: 16,
color: 'rgba(255,255,255,0.4)',
borderRadius: 50,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.4)',
borderStyle: 'solid',
height: 26,
width: 26,
paddingLeft: 9,
paddingTop: 2,
}
This is how the tooltip should look like (not the actual location):
And this is how it looks like now:
I have a problem with my react native layout iPhone 5c.
But iPhone 7 and iPad look good.
How to fix it?
My buttons code:
_renderButton(title, onPress, active) {
var style = (active) ? styles.activeButtonText : styles.buttonText;
return (
<TouchableHighlight style={styles.button} onPress={onPress}>
<Text style={style}>
{title}
</Text>
</TouchableHighlight>
);
}
render() {
return (
<View style={styles.container}>
<View style={styles.controls}>
{this._renderButton("LISTEN", () => {this._playOriginalAudio()})}
{this._renderButton(this.state.recordingText, () => {this._record()}, this.state.recording )}
{this._renderButton("LISTEN TO RECORDING", () => {this._play()} )}
{this._renderButton("SUBMIT", () => {this._send()} )}
</View>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#2b608a",
},
controls: {
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
button: {
padding: 20
},
disabledButtonText: {
color: '#eee'
},
buttonText: {
fontSize: 20,
color: "#fff"
},
activeButtonText: {
fontSize: 20,
color: "#B81F00"
},
points: {
backgroundColor: 'transparent',
position: 'absolute',
top: 72,
left: 56,
width: 90,
textAlign: 'center',
color: '#69d2e7',
fontSize: 50,
fontWeight: "100"
},
headerText: {
paddingBottom: 10,
fontSize: 20,
color: "#69d2e7"
}
});
I've tried to test app with iPhone 5 simulator, and I have no any style problem. Looks like the problem only with real iPhone 5 device. Maybe it is bug.
It might help to see your specific style sheet, but I am guessing you would benefit by importing the Dimensions module that React Native provides.
Dimensions Module
Then you can get the height and width of the device your app is running on, to change the styles appropriately.