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}]}>
Related
I'm quite new to react native and javascript and I have to make this type of progress bar with the four categories.
Can anyone help or suggest to me how can I achieve this UI in react native for android and iOS on both platforms?
The expected output screenshot is attached below:
However, I don't need the user interaction or progress animation, I just have to show the fix progress in percentage.
Here's an approximation of your component built in React Native, you'll need to add react-native-linear-gradient to your dependencies beforehand
I have also created a demo on Expo (you should test it on the target device) that uses expo's version of the linear gradient library. https://snack.expo.dev/#sabihi/milestone-progressbar
import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
export default function App() {
return (
<View style={styles.container}>
<View style={styles.progressLabels}>
<Text style={styles.progressLabel}>Beginner</Text>
<View style={styles.progressLabel}></View>
<Text style={styles.progressLabel}>Amateur</Text>
<View style={styles.progressLabel}></View>
<Text style={styles.progressLabel}>Elite</Text>
<View style={styles.progressLabel}></View>
<Text style={styles.progressLabel}>Master</Text>
</View>
<View style={styles.progressContainer}>
<LinearGradient colors={["rgb(7, 14, 60)", "rgb(86, 90, 106)"]} style={styles.progress}/>
<View style={styles.milestonesContainer}>
<View style={styles.milestone}/><View style={styles.milestone}/><View style={styles.milestone}/>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
// paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
progressLabels: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "100%",
marginBottom: 10,
},
progressLabel: {
fontSize: 12,
fontWeight: 'bold',
fontFamily: "sans-serif",
width: "14.3%",
textAlign: "center"
},
progressContainer: {
position: "relative",
height: 100
},
progress: {
marginLeft: "5%",
marginTop: 5,
width: "90%",
height: 30,
// backgroundColor: "rgb(7, 14, 60)",
borderRadius: 15
},
milestonesContainer: {
marginLeft: "5%",
position: "absolute",
width: "100%",
display: "flex",
flexDirection: "row"
},
milestone: {
height: 40,
width: "30%",
// backgroundColor: "white",
borderColor: "rgb(7, 14, 60)",
borderLeftWidth: 1,
borderRightWidth: 1,
}
});
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 >
I am trying to code one dynamic flatlist modal gallery in react native but I do not know where to start, I have added gif file for the output I am looking. It will be very helpful if any body could share the code or give some idea how to go about.
Thanking in advance.
Working Example: Expo Snack
Here is the working code:
import React, { useState } from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
TouchableOpacity,
Modal,
Alert,
} from 'react-native';
import Constants from 'expo-constants';
let img = ['one', 'two', 'three', 'four', 'five', 'six', 'seven'];
export default function App() {
const [selected, setSelected] = useState(null);
return (
<View style={styles.container}>
<FlatList
keyExtractor={(item) => item}
numColumns={3}
data={img}
renderItem={({ item, index }) => (
<TouchableOpacity onPress={() => setSelected(index)}>
<View style={styles.thumb}>
<Text>{item}</Text>
</View>
</TouchableOpacity>
)}
/>
<Modal
animationType="slide"
transparent={true}
visible={selected !== null}
onRequestClose={() => {
this.setSelected(null);
}}>
<View style={styles.preview}>
<TouchableOpacity onPress={() => setSelected(null)}>
<View style={styles.button}>
<Text>{'X'}</Text>
</View>
</TouchableOpacity>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'rgba(00, 00, 255, 0.4)',
flex: 1,
marginVertical: 50,
marginHorizontal: 10,
}}>
<Text>{img[selected]}</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
position: 'fixed',
left: 0,
right: 0,
}}>
<TouchableOpacity
onPress={() => {
if (selected > 0) {
setSelected(selected - 1);
}
}}>
<View
style={[
styles.button,
{ width: 30, height: 30, borderRadius: 15 },
]}>
<Text>{'<'}</Text>
</View>
</TouchableOpacity>
<TouchableOpacity
onPress={() => {
if (selected < img.length - 1) {
setSelected(selected + 1);
}
}}>
<View
style={[
styles.button,
{ width: 30, height: 30, borderRadius: 15 },
]}>
<Text>{'>'}</Text>
</View>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
thumb: {
backgroundColor: 'red',
width: 100,
height: 100,
margin: 5,
justifyContent: 'center',
alignItems: 'center',
},
preview: {
borderRadius: 10,
position: 'absolute',
bottom: 20,
top: 20,
left: 10,
right: 10,
backgroundColor: 'rgba(52, 52, 52, 0.8)',
},
button: {
width: 40,
height: 40,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 20,
backgroundColor: 'rgba(255, 255, 255, 0.5)',
margin: 10,
},
});
Put your image urls in to an array.
When pressed on the image, set the selected image index and set the modal visible.
Show the image in the modal from the array using the selected index.
Left - Right arrows can increase/decrease the selected image index so the image changes.
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)
},
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