fix position in RN StyleSheet - javascript

I am rendering an item in a list like this:
return (
<View style={styles.item}>
<View style={styles.nameAddressContainer}>
<Text style={styles.customisedName}>{place.customisedName}</Text>
<Text style={styles.address}>{place.placeName}</Text>
</View>
<View style={styles.deleteButtonContainer}>
<Button
rounded
style={styles.deleteButton}
onPress={() => onDeletePlace(place.id)}>
<Icon name="trash-o" size={moderateScale(15)} color="black" />
</Button>
</View>
</View>
);
export const styles = StyleSheet.create({
item: {
backgroundColor: '#d7fce4',
borderRadius: moderateScale(20),
marginVertical: moderateScale(8),
marginHorizontal: 16,
height: moderateScale(60),
justifyContent: 'space-between',
flexDirection: 'row',
},
customisedName: {
paddingTop: moderateScale(10),
},
deleteButton: {
backgroundColor: 'white',
width: moderateScale(35),
height: moderateScale(35),
justifyContent: 'center',
},
deleteButtonContainer: {
paddingTop: 7,
paddingRight: 7,
position: 'relative',
},
nameAddressContainer: {
flexDirection: 'column',
paddingLeft: moderateScale(10),
},
address: {
fontSize: moderateScale(9),
},
});
When the placeName text is too long, it pushes the button towards the right. The button moves out of the main green view. How can I stop this from happening? Is it possible to fix the button's position while the placeName text hides or moves to the next line?
position: fix is invalid for React Native stylesheets. I have also tried using zIndex but that didn't work either.

Add CSS for nameAddressContainer as
style = {{ flex : 1}}

Put your "deleteButtonContainer" style in "absolute" and add marginRight to your placeName equal to or slightly larger than the width of your DeleteButton.
Something like that:
...,
deleteButtonContainer: {
right: 8,
top: 8,
position: 'absolute',
},
...,
address: {
fontSize: moderateScale(9),
marginRight: moderateScale(38)
},

Related

Absolute positioning causes React Native component to go off screen

One of my React Native screens needs to have a TouchableOpacity at the bottom of the screen. I've tried setting position: "absolute" and bottom: 0, however, this just causes the element to be a few hundred pixels below the screen. How can I make my TouchableOpacity be at the bottom of the screen all the time?
<View style={styles.mainView}>
<View style={{ position: "relative" }}>
<TouchableOpacity style={styles.cartView}>
<Text style={styles.cartText}>View cart</Text>
</ViewTouchableOpacity
</View>
}
/>
</View>
//styles
const styles = StyleSheet.create({
mainView: {
// devDims are the device dimensions
minHeight: devDims.height,
minWidth: devDims.width,
backgroundColor: "white",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
},
cartView: {
justifyContent: "center",
alignItems: "center",
maxHeight: 50,
minWidth: "100%",
alignSelf: "center",
marginTop: 50,
padding: 10,
borderRadius: 20,
},
cartText: {
fontFamily: "semi",
fontSize: 22,
},
});
We could handle this without absolute positioning using flex alone.
Add flex:1 to the parent view and to the view that wraps the sticky bottom. Then, add flex: 2 to the view that wraps the other content.
Here is a minimal generic component that lets you add a sticky bottom component.
const MainScreen = ({bottom, children}) => {
return <View style={{flex: 1, backgroundColor: 'red'}}>
<View style={{flex: 2, backgroundColor: 'green'}}>
{children}
</View>
<View style={{flex: 1, backgroundColor: 'yellow'}}>
{bottom}
</View>
</View>
}
export default function App() {
return <MainScreen bottom={
<TouchableOpacity style={styles.cartView}>
<Text style={styles.cartText}>View cart</Text>
</TouchableOpacity>}>
</MainScreen>
}
The result looks as follows:
However, we can use absolute positioning as well. You are just missing the flex: 1 for the parent view.
export default function App() {
return <View style={styles.mainView}>
<View style={{ position: "absolute", bottom: 0 }}>
<TouchableOpacity style={styles.cartView}>
<Text style={styles.cartText}>View cart</Text>
</TouchableOpacity>
</View>
</View>
}
const styles = StyleSheet.create({
mainView: {
flex: 1,
// devDims are the device dimensions
minHeight: devDims.height,
minWidth: devDims.width,
backgroundColor: "red",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
},
cartView: {
justifyContent: "center",
alignItems: "center",
maxHeight: 50,
minWidth: "100%",
backgroundColor: "yellow",
alignSelf: "center",
marginTop: 50,
padding: 10,
borderRadius: 20,
},
cartText: {
fontFamily: "semi",
fontSize: 22,
},
});
The result is as follows:
if you are using position as absolute then your view must be last view before last closed view tag
When you have declared any view at bottom of the screen then you should be do like this
import {View, SafeAreaView} from 'react-native';
<SafeAreaView style={{flex:1}}>
<View style={{flex:1}}>
<!---Anything extra views---!>
<View style={{bottom:0,position:'absolute',start:0,end:0}}> !-- you're bottom view
<TouchableOpacity style={styles.cartView}>
<Text style={styles.cartText}>View cart</Text>
</ViewTouchableOpacity
</View>
</View>
</SafeAreaView >

put icon and text in same line, opposite ends

I know similar questions have been asked before but I couldn't fix this myself. I have an icon and a text that should be displayed on both ends of the screen. The icon should be at the left end while the text should be at the right end.
However, the Text extends the screen and is hidden from the right side, even if I add marginRight or paddingRight. How can I fix this?
export default function App() {
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<View style={styles.container}>
<View style={styles.topTextContainer}>
<BackArrow containerStyles={styles.arrowContainer} arrowStyles={styles.arrow}></BackArrow>
<Text style={styles.allFavoritePlaces}>Alle Lieblingsorts</Text>
</View>
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
arrowContainer: {
marginTop: 0,
marginBottom: 0,
marginLeft: 0,
},
arrow: {
color: 'black',
paddingTop: 0,
},
allFavoritePlaces: {
alignItems: 'flex-end',
paddingRight: moderateScale(0),
paddingBottom: moderateScale(10),
},
topTextContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginVertical: moderateScale(15),
height: moderateScale(30),
paddingHorizontal: 20,
},
});
Here's a codesandbox too: https://snack.expo.io/#nhammad/sponaneous-candy
I opend it in the dev tools. The container of your arrow has some width assigned to it, when the screen gets to small it pushes the text out of the viewport.

React Native TouchableOpacity not working with position absolute

I have a list that shows results matching the user's input. The onPress of the touchableOpacity is not working in this list. This list is positioned absolutely and positioned below its parent view (positioned relative). The only time I can get the onPress to work is when I remove the top:48 style from list and the onPress works for the single element which is directly onTop of the parent.
export default function IndoorForm(props) {
return (
<View style={styles.container}>
<View style={styles.parent}>
<Autocomplete
style={styles.autocomplete}
autoCompleteValues={autoCompleteValues}
selectedLocation={props.getDestination}
></Autocomplete>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignSelf: "center",
position: "absolute",
top: Platform.OS === "android" ? 25 + 48 : 0 + 48,
width: Dimensions.get("window").width - 30,
zIndex: 500
},
parent: {
position: "relative",
flex: 1,
borderWidth: 2,
borderColor: "#AA2B45",
height: 48,
backgroundColor: "#fff",
flexDirection: "row",
alignItems: "center",
paddingLeft: 16,
paddingRight: 16,
justifyContent: "space-between"
}
}
export default function AutoComplete(props: AutoCompleteProps) {
const { autoCompleteValues } = props;
return (
<View style={styles.container}>
<FlatList
data={autoCompleteValues}
renderItem={({ item }: { item: POI }) => (
<TouchableOpacity onPress={() => console.log("Haal")} key={item.displayName} style={styles.list}>
<Text style={styles.text}>{item.displayName}</Text>
<Entypo name={"chevron-thin-right"} size={24} color={"#454F63"} />
</TouchableOpacity>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
position: "absolute",
flex: 1,
width: Dimensions.get("window").width - 30,
top: 48,
borderWidth: 2,
borderColor: "#F7F7FA",
backgroundColor: "#F7F7FA",
zIndex: 999
},
list: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingTop: 15,
paddingLeft: 10,
paddingBottom: 10,
borderBottomColor: "rgba(120, 132, 158, 0.08)",
borderBottomWidth: 1.4,
zIndex: 999
}
}
I know you solved your issue already, but you can use this magical library react-native-gesture-handler, import your Touchables from there and they don't care about being inside the parent views. You can touch them regardless.
Resolved this by dynamically adjusting the container height so that the touchableOpacity is within the container. The issue was I positioned the list outside of the parent (as intended by styling) but for onPress to work it has to be inside the parent.
let autocompleteHeight = autoCompleteValues.length * 65
<View style={[styles.container, {height: autocompleteHeight}]}>

How can I horizontally wrap a list of dynamic tags in a ScrollView?

I've created a list of tags in React Native, but I can't seem to make them wrap across multiple rows — here's what I have so far:
I want the tags to expand depending on the text - for example, the last tag in the screenshot should say Another tag - and wrap to a new line when needed.
I've rendered the tags using a ScrollView component, as I understand FlatList doesn't support wrapping - here's that code:
<ScrollView
contentContainerStyle={{
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
}}
style={styles.tagContainer}>
{selectedTags
? selectedTags.map(tag => (
<Tag
style={{backgroundColor: tag.color}}
label={tag.label}
isList={false}
/>
))
: null}
</ScrollView>
styles: {
tagContainer: {
width: SCREEN_WIDTH - 20,
minHeight: 75,
},
}
Each tag is rendered using a Tag component that looks like this:
const Tag = ({label, style, isList, isChecked}) => {
const renderIcon = () => {
if (isList === true) {
return (
<Icon
name="ios-checkmark"
color={isChecked ? 'black' : 'white'}
size={36}
style={{alignSelf: 'center', margin: 5}}
/>
);
}
};
return (
<View
style={{
flex: 1,
flexDirection: 'row',
}}>
{renderIcon()}
<View style={isList ? styles.listContainer : styles.tagContainer}>
<Text style={[styles.tagStyle, style]}>{label}</Text>
</View>
</View>
);
};
const styles = {
tagContainer: {margin: 2, height: 30, flex: 1},
listContainer: {
flex: 1,
flexDirection: 'column',
width: 375,
height: 50,
borderBottomColor: '#dadade',
borderBottomWidth: 0.5,
justifyContent: 'center',
},
tagStyle: {
borderRadius: 4,
padding: 5,
marginLeft: 10,
flexDirection: 'row',
alignSelf: 'flex-start',
},
};
export {Tag};
Thanks!
Try below
<View style={{ flexDirection: 'row', flexWrap: 'wrap', flex: 1, alignItems: 'flex-start' }}>
{selectedTags
? selectedTags.map(tag => (
<Tag
style={{backgroundColor: tag.color}}
label={tag.label}
isList={false}
/>
))
: null}
</View>

flex:1 not filling whole screen

I'm trying to change the background color of my app, but the color doesn't fill the whole screen. When I set flex to 0, the background color only spans the needed amount, but when I set it to 1, instead of expanding the whole screen, it shrinks. What did I do wrong?
Here's the code, changing the root view flex:
render() {
return (
<Text>{welcome}</Text>
<TextInput
underlineColorAndroid="rgba(0,0,0,0)"
placeholder={email}
autoCapitalize="none"
style={styles.textInput}
onChangeText={email1 => this.setState({ email1 })}
/>
<TextInput
underlineColorAndroid="rgba(0,0,0,0)"
secureTextEntry
placeholder={password}
autoCapitalize="none"
style={styles.textInput}
onChangeText={password1 => this.setState({ password1 })}
value={this.state.password1}
/>
{this.state.errorMessage && (
<Text style={styles.error}>{this.state.errorMessage}</Text>
)}
<TouchableOpacity
onPress={this.handleSignUp}
style={styles.buttonContainer}
>
<Text style={styles.buttonText}>{signup}</Text>
</TouchableOpacity>
<Text
style={styles.text}
onPress={() => this.props.navigation.navigate("LoginScreen")}
>
{already_have_an_account}
</Text>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: "center",
padding: 20,
backgroundColor: "red",
flex: 0
},
textInput: {
height: 40,
marginTop: 8,
paddingHorizontal: 8,
backgroundColor: "#e2e2e2"
},
buttonContainer: {
backgroundColor: "#3bd774",
padding: 15,
marginTop: 10
},
buttonText: {
textAlign: "center",
color: "white",
fontWeight: "bold"
},
logo: {
width: 200,
height: 200
},
logoContainer: {
alignItems: "center",
justifyContent: "center"
},
text: {
marginTop: 16,
color: "#bcbcbc",
textAlign: "center"
},
error: {
marginTop: 8,
color: "red"
}
});
You need to make sure you have flex:1 all the way down the chain of views. This is because flex: 1 will make the view fill its parent
I can see that what you have copy/pasted isn't all of the code, since there is a </KeyboardAvoidingView> but no opening tag.
So, at the very least you need a <KeyboardAvoidingView style={{flex: 1}}>, but if there is something wrapping that, you should add flex:1 to that control as well.
Put whole layout in a View wrapper and give this style to this view
container: {
justifyContent: "center",
padding: 20,
alignItems:'center',
backgroundColor: "red",
flex: 1
}
Running example: https://snack.expo.io/Hy-Gv-lGm

Categories