I am creating a render method where it displays information from a state array, and I want it so that when a user touches a Card a Modal will open presenting the same information.
My code is as follows:
this.state = {
fontLoaded: false,
feed: [{
username: ["Jeff", "Kyle", "David"],
caption: ["Great", "Amazing", "Not so Good"],
comments: ["Comment 1", "Comment 2", "No Comment"],
photo:[Pic1,Pic2,Pic3],
}]
}
state = {
isModalVisible: false,
};
toggleModal = () => {
this.setState({ isModalVisible: !this.state.isModalVisible });
};
renderFeed = () => {
return this.state.feed.map((card, index) => {
return card.username.map((username, i) => {
if(card.caption[i])
return (
<View>
<TouchableHighlight onPress={this.toggleModal} underlayColor="white">
<Card
key={`${i}_${index}`}
image={card.photo[i]}
containerStyle={{borderRadius:10, marginRight:1, marginLeft:1,}}>
<View
style={{ flex: 1, flexDirection: 'row', justifyContent: 'space-between' }}
>
<View style={{ flexDirection: 'row'}}>
<Avatar
size="small"
rounded
source={card.photo[i]}
/>
</View>
<View style={{flexDirection:'row'}}>
<Avatar
rounded
icon={{ name:'heart-multiple-outline', type:'material-community', color: '#ff4284'}}
overlayContainerStyle={{marginLeft:5}}
reverse
size='small'/>
<Avatar
reverse
rounded
icon={{ name:'comment-processing-outline', type:'material-community' }}
overlayContainerStyle={{backgroundColor: '#dbdbdb',marginLeft:5}}
size='small'/>
</View>
</View>
{ this.state.fontLoaded == true ? (
<View style={{flexDirection:'row'}}>
<Text style={{fontFamily: 'MontserratB', color:'#bf00b9', marginTop:10}}>{username}</Text>
<Text style={{fontFamily:'Montserrat', marginTop:10}}>{card.caption[i]}</Text>
</View>
) : (<Text> Loading...</Text>)
}
<Text style={{marginTop:4, color:'#878787'}}>{card.comments[i]}</Text>
</Card>
</TouchableHighlight>
<Modal
animationIn="zoomInDown"
animationOut="zoomOutDown"
animationInTiming={700}
animationOutTiming={600}
backdropTransitionInTiming={600}
backdropTransitionOutTiming={600}
isVisible={this.state.isModalVisible}>
<Image source={card.photo[i]}
style={{width: SCREEN_WIDTH - 20,
height: 300, justifyContent: 'center', alignSelf:
'center' }}/>
<Card
containerStyle={{ width: SCREEN_WIDTH - 20, marginTop: 0, justifyContent: 'center', alignSelf:
'center' }}>
<View style={{ flexDirection:'row' }}>
<Avatar
size="small"
rounded
source={card.photo[i]}
/>
<View style={{ flex:2, flexDirection:'row', justifyContent:'flex-end' }}>
<Avatar
rounded
icon={{ name:'heart-multiple-outline', type:'material-community'}}
overlayContainerStyle={{backgroundColor: '#ff4284',marginLeft:5}}
reverse
size='small'/>
<Avatar
reverse
rounded
icon={{ name:'comment-processing-outline', type:'material-community' }}
overlayContainerStyle={{backgroundColor: '#dbdbdb',marginLeft:5}}
size='small'/>
</View>
</View>
<View style={{ flexDirection:'row' }}>
<Text style={{fontFamily: 'MontserratB', color:'#bf00b9', marginTop:10}}>{username}</Text>
<Text style={{fontFamily:'Montserrat', marginTop:10}}>{card.caption[i]}</Text>
</View>
<Text style={{marginTop:4, color:'#878787'}}>{card.comments[i]}</Text>
</Card>
<Button style={{marginTop:0, borderBottomRightRadius: 20, borderBottomLeftRadius: 20, borderTopLeftRadius: 0, borderTopRightRadius: 0, width: SCREEN_WIDTH - 20, alignSelf: 'center', justifyContent: 'center'}} title="Close" onPress={this.toggleModal} />
</Modal>
</View>
);
return <React.Fragment />;
});
})
}
everything works perfectly except that no matter which Card is touched a Modal is opened presenting the last objects in the array. So no matter which of the three cards are pressed, a Modal will always open with the information regarding David
Modified snack version of your sample:
https://snack.expo.io/H1yHPQQdr
Related
My app have structure like this,
MapPage.js that contain expo-barcode-scanner wrapped with Modal and MapView.
DetailsPage.js that contain search result/products' details.
User can use the barcode scanner or TextInput(also in MapPage, not wrapped in Modal) to search product ID and the app will navigate to DetailsPage for result. If result is not found, app will automatically navigate.goBack() to MapPage. If result existed, will goto the result and stay in DetailsPage.
User can also navigate to DetailsPage by pressing marker callout on MapView.
Here is situation:
user press on marker callout(navigate to DetailsPage), then press back(navigate.goBack()) to MapPage : No lagging
user search by TextInput, then back to MapPage: No lagging whether search result exist or not
user search by Barcode, result not exist, automatically back to MapPage: not lagging
user search by Barcode, result existed, stay in DetailsPage, user manually press back: LAGGING
Below codes for your references
//MapPage
<MapView
style={{ width: '100%', height: '100%' }}
provider={PROVIDER_GOOGLE}
showsMyLocationButton={true}
showsUserLocation={true}
region={{
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta,
longitudeDelta: region.longitudeDelta,
}}>
<MapMarker
markerLocation={markers}
countryCode={country.countryCode}
/>
</MapView>
...
<TextInput
placeholder={
i18n.t('SearchId')
}
placeholderTextColor="white"
onSubmitEditing={() =>
handleBarCodeScanned({ data: textSearchData })
}
onChangeText={(text) => {
settextSearchData(text.toUpperCase());
}}
/>
<Pressable onPress={() => setqrcodeModalVisi(true)}>
<Ionicons
name="qr-code-outline"
size={33}
color="white"
/>
</Pressable>
...
<Modal
visible={qrcodeModalVisi}
animationType="slide"
onRequestClose={() => setqrcodeModalVisi(false)}
transparent={true}>
<View>
<Pressable onPress={() => setqrcodeModalVisi(false)}>
<Feather
name="x"
size={24}
color="white"
/>
</Pressable>
{!hasPermission && (
<View>
<Text
style={{
color: colors.text,
}}>
Camera access denied.
</Text>
<Pressable onPress={() => getBarCodeScannerPermissions()}>
<Text style={{ color: colors.secondary }}>
Goto setting page
</Text>
</Pressable>
</View>
)}
{hasPermission && (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<BarCodeScanner
onBarCodeScanned={
scanned ? undefined : handleBarCodeScanned
}
style={{
width: '100%',
height: '80%',
}}
/>
</View>
)}
</View>
</Modal>
//MapMarker
<View>
{markerLocation.map((array) => (
<Marker
key={array.title}
coordinate={{
latitude: array.latitude,
longitude: array.longitude,
}}
image={require('../../../assets/icons/map_marker.png')}
title={''}
description={''}
onCalloutPress={() => handleMapMarkerPress(array.regionCode)}>
<Callout>
<View
style={{
backgroundColor: 'white',
width: 180,
paddingVertical: 5,
alignItems: 'center',
justifyContent: 'center',
}}>
<Text
style={{
width: '80%',
textAlign: 'center',
color: colors.secondary,
fontWeight: 'bold',
}}>
{array.title}
</Text>
<View
style={{
backgroundColor: colors.secondary,
width: '80%',
height: 25,
alignItems: 'center',
justifyContent: 'center',
marginTop: 2,
flexDirection: 'row',
marginVertical: 5,
}}>
<Text style={{ color: 'white', fontWeight: 'bold' }}>
{i18n.t('MapMarkerBtn')}
</Text>
</View>
</View>
</Callout>
</Marker>
))}
</View>
Thanks!
The first row shows a single card, this is because the item suppose to be there doesn't match the condition stated. But I want to remove this space, and put the next card which is currently on the next line there.
{state.data ? <FlatList
data={state.data}
renderItem={({ item }) => {
console.log(item)
return (
<View style={{ justifyContent: 'space-evenly', marginHorizontal: 0 }}>
<View>
{item.available == 1 ?
<TouchableOpacity activeOpacity={.8} onPress={() => navigation.navigate('Appointment', { id: item.id, name: item.name, phone: item.phone, email: item.email, session: item.session, player_id: item.player_id, picture: item.picture })} >
<Card style={{ width: width / 2.05, alignItems: 'center', justifyContent: 'center', borderRadius: 15, paddingTop: 10, height: height / 2.4 }}>
<CardItem cardBody>
<Image
source={{ uri: 'http://example.com/storage/' + item.picture }}
style={{ height: 200, width: null, flex: 1 }}
/>
</CardItem>
<CardItem>
<Text> {item.name} </Text>
</CardItem>
</Card>
</TouchableOpacity>
: null}
</View>
</View>
)
}}
numColumns={2}
/> : <ActivityIndicator size="large" style={{ alignItems: 'center', justifyContent: 'center', flex: 1, flexDirection: 'row' }} />}
The null value just shows that empty space, is there a way to completely remove this empty space?
You need to filter your data. Otherwise, your code will create empty Views.
Try to filter your data as follows.
It would be good if you move this filter logic from your render method.
{state.data ? <FlatList
data={state.data.filter(item => item && item.available === 1)}
renderItem={({ item }) => {
return (
<View style={{ justifyContent: 'space-evenly', marginHorizontal: 0 }}>
<TouchableOpacity activeOpacity={.8}
onPress={() => navigation.navigate('Appointment', { id: item.id, name: item.name, phone: item.phone, email: item.email, session: item.session, player_id: item.player_id, picture: item.picture })} >
<Card style={{ width: width / 2.05, alignItems: 'center', justifyContent: 'center', borderRadius: 15, paddingTop: 10, height: height / 2.4 }}>
<CardItem cardBody>
<Image
source={{ uri: 'http://example.com/storage/' + item.picture }}
style={{ height: 200, width: null, flex: 1 }}
/>
</CardItem>
<CardItem>
<Text> {item.name} </Text>
</CardItem>
</Card>
</TouchableOpacity>
</View>
)
}}
numColumns={2}
/> : <ActivityIndicator size="large" style={{ alignItems: 'center', justifyContent: 'center', flex: 1, flexDirection: 'row' }} />}
Hi how can I first show the user that I've clicked on and then display a swiper ? I can't find a logic for that. I have a list of users and when I click on one, it should open a new component where we will find more infos about that user. Then I want to swipe between the list of users on the same component of the description ?
Here are my user profile component and the flatlist :
render () {
var colorConnected;
if (this.props.navigation.getParam('Statut') === "ON") {
colorConnected = "#1fbc26";
}
else if (this.props.navigation.getParam('Statut') === "OFF") {
colorConnected = "#ff0303";
}
else {
colorConnected = "#ffd200";
}
return (
<Swiper showsPagination={false}>
{this.state.items.map((item, key) => {
return (
<ScrollView style = {styles.view_container}>
<View style={styles.photo}>
<ImageBackground source={{uri:this.props.navigation.getParam('Photo')}} style={{ width: '100%', height: '100%' }}>
<View style={styles.photo_content}>
<LinearGradient colors={['transparent', 'rgba(0,0,0,0.5)']} style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: 80 }} />
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<View>
<Text style={{ fontSize: 40, color:'white' }}>{this.props.navigation.getParam('Age')}</Text>
</View>
<View style={{ marginRight: 7, marginLeft: 7, backgroundColor: '#ffffff', width: 1, height: 39 }}></View>
<View style={{ flexDirection: 'column', flex:1 }}>
<View style={{ flexDirection: 'row' }}>
<View style={[styles.bulle_presence, { backgroundColor: colorConnected } ]}></View>
<Text style={{ fontSize: 18, fontWeight: '600', color:'white' }}>{this.props.navigation.getParam('Pseudo')}</Text>
</View>
<View style={{ flexDirection: 'row', justifyContent: 'space-between'}}>
<Text style={{ fontSize: 15, color:'white' }}>{this.props.navigation.getParam('Distance')}</Text>
<Text style={{ fontSize: 15, color:'white'}}>{this.props.navigation.getParam('Genre')}</Text>
</View>
</View>
</View>
</View>
</ImageBackgroud>
</View>
</ScrollView>
)
})}
</Swiper>
)}
}
render() {
GLOBAL.data = this.state.dataSource;
//console.log(GLOBAL.data);
return (
<SafeAreaView style={{ flex:1 }}>
<View style={styles.main_container}>
<FlatList style={styles.flatList}
data={this.state.dataSource}
extraData = {this.state}
keyExtractor={(item, index) => item.MembreId}
renderItem={(item) => <UserItem user={item} displayDetailForUser={this._displayDetailForUser} />}
numColumns={numColumns}
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh} />
</View>
</SafeAreaView>
)
}
With this code, I'm only able to swipe on the user where I've clicked. The others user are not showing.
You can open a modal then use a carousel like react-native-snap-carousel in full screen mode to generate your use case
I want to disable user interaction when the activity indicator is running. The user should not be able to type again in the TextInput and disable touchableopacity button.
render() {
return (
<View style={styles.container}>
<View style={styles.backgroundContainer}>
<Image
source={require("./background-image.jpg")}
resizeMode="cover"
style={styles.backdrop}
/>
</View>
<View style={styles.contentContainer}>
<View style={styles.subContainer}>
<Image style={styles.image} source={require("./logo.png")} />
<TextInput
// editable={!this.props.isLoginLoading}
style={styles.input}
ref={"input1"}
placeholder="Enter your mobile number"
maxLength={10}
onChangeText={value => this.setState({ mobilenumber: value })}
/>
<TouchableOpacity
// activeOpacity={this.getOpacity()} //newly added
onPress={this.onPress}
>
<View style={styles.button}>
<Text style={styles.buttonText}>Login</Text>
</View>
</TouchableOpacity>
</View>
{this.props.isLoginLoading && (
<View style={styles.indicator}>
<ActivityIndicator color={"blue"} />
</View>
)}
</View>
</View>
);
}
}
Below shows the design for the above code, styles.js
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
backgroundColor: "#FFFFFF"
},
backgroundContainer: {
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0
},
contentContainer: {
flex: 1,
alignItems: "center",
justifyContent: "center"
},
subContainer: {
alignItems: "center"
},
backdrop: {
flex: 1
},
image: {
marginBottom: Metrics.screenHeight * 0.075,
resizeMode: "contain",
width: Metrics.screenWidth * 0.35,
height: Metrics.screenHeight * 0.25
},
input: {
borderWidth: 1,
marginBottom: Metrics.screenWidth * 0.03,
width: Metrics.screenWidth * 0.626,
backgroundColor: "lightgrey"
},
button: {
width: Metrics.screenWidth * 0.626,
height: Metrics.screenHeight * 0.06,
alignItems: "center",
backgroundColor: "#8c0d04",
fontWeight: "bold"
},
buttonText: {
padding: Metrics.screenHeight * 0.0165,
color: "#FFFFFF",
fontWeight: "bold",
alignItems: "center",
justifyContent: "center"
},
indicator: {
position: "absolute",
left: 0,
right: 0,
top: 0,
bottom: 0,
// width: "100%",
// height: "100%",
justifyContent: "center",
alignItems: "center"
// borderWidth: 5,
// borderColor: "yellow"
}
});
export default styles;
I used dimensions to get the screen height and screen width according to the device size and added style to the activity indicator to by writing position to 'absolute' but still I'm able to access the text input as well the button click. I want to achieve the following thing when the user clicks the login button, I need to show the activity indicator below the Login button and I have to disable user interaction during the activity indicator.
There is two way you can achieve this.
If you want ActivityIndicator below of button then just remove view which contains {{flex:1}} style. and only give marginTop style to the parent view of ActivityIndicator. For that, you have to disable TextInput and button manually. Please see below code.
export default class Example extends Component {
onPress() {
if (!this.props.isLoginLoading) {
// skip if isLoginLoading is true
}
}
getOpacity() {
if (this.props.isLoginLoading) {
return 1;
}
return 0
}
render() {
return (
<View style={styles.container}>
<View style={styles.backgroundContainer}>
<Image source={require('./background-image.jpg')} resizeMode='cover' style={styles.backdrop} />
</View>
<View>
<View>
<Image
style={styles.image}
source={require('./logo.png')}
/>
</View>
<View style={styles.textinputview}>
<TextInput
editable={!this.props.isLoginLoading} //do editable false when loading is true
style={styles.input}
ref={'input1'}
placeholder='Enter your mobile number'
maxLength={10}
onChangeText={value => this.setState({ mobilenumber: value })}
/>
</View>
<View style={styles.touchableview}>
<TouchableOpacity
activeOpacity={this.getOpacity()} //set activopacity to 1 to indicate button is disable, when loading is true
onPress={this.onPress.bind(this)}>
<View style={styles.button}>
<Text style={styles.buttonText}>Login</Text>
</View>
</TouchableOpacity>
</View>
</View>
{this.props.isLoginLoading &&
<View
style={{
marginTop:10
}} >
<ActivityIndicator color={'blue'} />
</View>
}
</View>
);
}
}
just remove view which contains flex:{1} style. which will include ActivityIndicator in the center of the screen
return (
<View style={styles.backgroundContainer}>
<Image source={require('./background-image.jpg')} resizeMode='cover' style={styles.backdrop} />
</View>
<View>
<View>
<Image
style={styles.image}
source={require('./logo.png')}
/>
</View>
<View style={styles.textinputview}>
<TextInput
style={styles.input}
ref={'input1'}
placeholder='Enter your mobile number'
maxLength={10}
onChangeText={value => this.setState({ mobilenumber: value })}
/>
</View>
<View style={styles.touchableview}>
<TouchableOpacity onPress={this.onPress}>
<View style={styles.button}>
<Text style={styles.buttonText}>Login</Text>
</View>
</TouchableOpacity>
</View>
</View>
{this.props.isLoginLoading &&
<View
style={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
width:'100%',
height:'100%',
justifyContent: 'center',
alignItems: 'center'
}}>
<ActivityIndicator color={'blue'} />
</View>
}
</View>
)
I am new to react native, In my application I have list. So that i have taken flatlist. I have designed the list item. But arrow icon need to set end of the item. I have tried many ways how to do this.
This is the my code
<View style={styles.MainContainer}>
<FlatList
data={ this.state.FlatListItems }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={
({item}) =>
<View style={styles.main}>
<View style={styles.itemContainer}>
<View>
<Image source={require('./resource/ic_drawer.png')} />
<Text style={styles.item} onPress={this.GetItem.bind(this, item.key)} >
{item.key}
</Text>
</View>
<View style={styles.balanceItem}>
<View>
<Text >Balance</Text>
<Text >$89.04</Text>
</View>
<View style={styles.subItem}>
<View>
<Text >Account number</Text>
<Text >743509-001</Text>
</View>
<View style={styles.balanceItem}>
<Text >Meter number</Text>
<Text >17976849</Text>
</View>
</View>
</View>
<View style={styles.balanceItem}>
<View style={styles.duenbuttonItem}>
<View>
<Text >Due Date</Text>
<Text >30/09/2016</Text>
</View>
</View>
<TouchableOpacity style={styles.btn} onPress={this.login}><Text style={{color: 'white'}}>PAY NOW</Text></TouchableOpacity>
</View>
</View>
<Image source={require('./resource/arrow_24.png')} style={styles.arrowImage}/>
</View>
}
/>
</View>
and this is my styles
const styles = StyleSheet.create({
MainContainer :{
// Setting up View inside content in Vertically center.
justifyContent: 'center',
flex:1,
margin: 10
},
item: {
padding: 0,
fontSize: 18,
},
itemContainer :{
flex: 1,
flexDirection: 'row',
padding : 10,
},
subItem: {
marginTop: 10,
flexDirection: 'row',
},
btn:{
borderRadius: 10,
alignSelf: 'stretch',
backgroundColor: '#003366',
padding: 10,
marginTop: 10,
alignItems: 'center',
},
balanceItem: {
marginLeft: 5,
},
duenbuttonItem: {
marginLeft: 10,
flexDirection: 'row',
},
arrowImage: {
justifyContent: 'flex-end',
justifyContent: 'center',
},
main:{
flex: 1,
}
});
And this the output of the above code
Here my requirement is the arrow image end of the item in center. So please guide me how to do this?