Related
I'm with a problem with TouchableOpacity and Negative Margins inside a FlatList. On iOS works well, but on Android, when I click at the TouchableOpacity in the front of other TochableOpacity, the TouchableOpacity from behind fires. I don't know how to solve this.
iOS Image
I clicked at "Proposta 70" but fires "Proposta 78" from behind.
Android Image
The FlatList code
<View style={styles.containerList}>
<FlatList
data={proposalsList}
keyExtractor={item => item.proposta_id}
renderItem={({ item, index }) => (
<RenderItem
item={item}
index={index}
isLoweredCard={
openedCardIndex !== null && index === openedCardIndex + 1
}
changeOpenedCardIndex={changeOpenedCardIndex}
/>
)}
refreshing={loading}
onRefresh={() => getProposalsAndNotifications()}
/>
</View>
The RenderItem code
<TouchableOpacity
style={styles.container(index, isLoweredCard)}
onPress={() => changeOpenedCardIndex(index)}
>
<>
<View
style={[
styles.lineContainer,
{ marginBottom: metrics.padding * 1.5 },
]}
>
<View
style={{
width: '50%',
}}
>
<Text style={styles.proposalId}>
{`Proposta ${item.proposta_id}`}
</Text>
<Text style={styles.proposalDate}>
{dayjs(item?.proposta_data_criacao).format('DD.MM.YYYY')}
</Text>
</View>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'flex-end',
}}
>
<View style={styles.statusContainer}>
<Text style={{ fontSize: wp(4), fontWeight: 'bold' }}>
<TypeStatus status={item?.proposta_status} />
</Text>
</View>
</View>
</View>
<View style={styles.lineContainer}>
<Text style={styles.proposalDetailLabel}>Valor solicitado</Text>
<Text style={styles.proposalDetailValue}>
{formatCurrency(item?.proposta_valor_financiado)}
</Text>
</View>
<View style={styles.lineContainer}>
<Text style={styles.proposalDetailLabel}>Valor liberado</Text>
<Text style={styles.proposalDetailValue}>
{formatCurrency(item?.proposta_valor_financiado)}
</Text>
</View>
<View style={styles.lineContainer}>
<Text style={styles.proposalDetailLabel}>Parcelas</Text>
<Text style={styles.proposalDetailValue}>
{`${item?.proposta_valor_prazo}x`}
</Text>
</View>
<View style={styles.lineContainer}>
<Text style={styles.proposalDetailLabel}>Valor da parcela</Text>
<Text style={styles.proposalDetailValue}>
{formatCurrency(item?.proposta_valor_parcela)}
</Text>
</View>
<View style={styles.buttonContainer}>
<Button
onPress={goToDetails}
title="Ver detalhes"
titleStyle={styles.proposalButtonText}
style={styles.button}
/>
</View>
</>
</TouchableOpacity>
And the style of items
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from 'react-native-responsive-screen';
import { metrics, colors } from '../../../../constants';
const styles = StyleSheet.create({
container: (index, isLoweredCard) => ({
backgroundColor: `#00${index}F${index}C`,
marginTop: !isLoweredCard && index !== 0 ? -wp(53) : metrics.padding,
marginHorizontal: metrics.padding,
alignContent: 'center',
padding: metrics.padding,
borderRadius: metrics.radius,
zIndex: -(index + 999),
}),
lineContainer: {
width: '100%',
justifyContent: 'space-between',
flexDirection: 'row',
marginBottom: metrics.padding / 2,
},
statusContainer: {
backgroundColor: colors.white,
borderRadius: 20,
width: '70%',
paddingVertical: 3,
alignItems: 'center',
justifyContent: 'center',
},
proposalId: {
color: colors.white,
fontWeight: 'bold',
fontSize: wp(4.5),
},
proposalDate: {
color: 'rgba(0, 0, 0, 0.5)',
fontWeight: 'bold',
fontSize: wp(3.5),
},
proposalDetailLabel: {
fontSize: wp(4),
color: 'rgba(0, 0, 0, 0.9)',
},
proposalDetailValue: {
fontSize: wp(4.5),
color: colors.white,
fontWeight: 'bold',
},
proposalButtonText: {
color: colors.white,
fontWeight: 'bold',
fontSize: wp(4),
},
button: {
borderRadius: metrics.radius,
backgroundColor: '#002F6C',
paddingHorizontal: metrics.padding * 3,
},
buttonContainer: {
width: '100%',
marginTop: metrics.padding,
alignItems: 'center',
},
});
export default styles;
I encountered exactly the same problem on Android. My FlatList rendered items with negative margin to superimpose them on each other. The touchable on iOS is perfect but on Android it press the lowest item behind.
Finally I solved this by replacing
import { TouchableOpacity } from 'react-native';
with
import { TouchableOpacity } from 'react-native-gesture-handler';
Using react-native 0.63.4 and react-native-gesture-handler 1.9.0 in my case.
The only solution for my case was inverting the negative margin (top / bottom).
In my case the View component was with negative marginBottom and the touchable part that I wanted was on the bottom as well, so I inverted it and put the View inside the next component and gave a negative marginTop. So the part that I wanted to be touchable worked fine.
A little strange but for me it worked out well...
I wanna achieve what you can see in image below, but I have no idea to make flat list items overlap each other:
forget about the right and left icons, I'm just asking for a way for a middle container which has cards rendering on each other. tnx in advance
Working example: https://snack.expo.io/#msbot01/sponaneous-salsa
export default function App() {
return (
<View style={styles.container}>
<View style={{flexDirection:'row', justifyContent:'flex-end', backgroundColor:'white', paddingTop:10, paddingBottom:10}}>
<View style={{backgroundColor:'transparent'}}>
<View style={[styles.shadow, {backgroundColor:'red', borderTopLeftRadius:40, borderBottomLeftRadius:40}]}>
<Text style={styles.paragraph}>
First
</Text>
</View>
</View>
<View style={{backgroundColor:'red', borderBottomWidth:0, borderColor:'white'}}>
<View style={[styles.shadow, {backgroundColor:'red', borderTopLeftRadius:40, borderBottomLeftRadius:40}]}>
<Text style={styles.paragraph}>
second
</Text>
</View>
</View>
<View style={{backgroundColor:'red', borderBottomWidth:0, borderColor:'white'}}>
<View style={[styles.shadow, {backgroundColor:'red', borderTopLeftRadius:40, borderBottomLeftRadius:40}]}>
<Text style={styles.paragraph}>
third
</Text>
</View>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
shadow:{
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.8,
shadowRadius: 4,
elevation: 5
}
});
I have a component it has a view of flex: 1 with a status bar component and a child component, when the status bar element isn't there the indicator shows, but when I add the status bar element, the indicator goes hidden, Please how can i make the indicator show while the status bar is added, what is making it go hidden
With the StatusBar(The indicator goes hidden)
Without the StatusBar(The Indicator shows)
THE CODE
CHILD COMPONENT(On)
import {BoxShadow} from 'react-native-shadow';
export default class On extends Component {
render() {
const shadowOpt = {
width: Dimensions.get('window').width,
height: Dimensions.get('window').height / 10,
color: "#000",
border: 10,
opacity: '0.15',
radius: 20,
x: 0,
y: 5,
}
const dimensions = Dimensions.get('window');
const Height = (dimensions.height);
const Width = dimensions.width;
return (
<View style={{flex: 1}}><View name="indicator" style={{flexDirection: 'row',}}>
<View style={{backgroundColor: '#cbcdda', width: Width, height: Height / 60}}>
</View>
<View style={{
backgroundColor: '#EFB879',
width: (this.state.width),
height: Height / 60,
position: 'absolute'
}}>
</View>
</View>
<ViewPagerAndroid
onPageSelected={this.onPageSelected.bind(this)}
ref={(viewPager) => {
this.viewPager = viewPager
}}
style={{height: Height - ((Height / 30) + (Height / 10))}}
initialPage={0}>
<View style={{
backgroundColor: 'white',
alignItems: 'center',
alignSelf: 'center',
justifyContent: 'center',
flexDirection: 'column'
}} key="1">
<Image style={{marginBottom: 50,}} source={require('../on1.png')}/>
<Text style={{
fontFamily: 'mont',
color: '#000',
fontSize: 22,
letterSpacing: 0.5,
marginBottom: 12
}}>
Welcome to Sẹlẹ
</Text>
<Text style={{
fontFamily: 'mont',
color: '#615D5D',
fontSize: 16,
letterSpacing: 1,
textAlign: 'center'
}}>
Hire services and buy and sell {'\n'} on Sẹlẹ
</Text>
</View>
<View style={{
backgroundColor: 'white',
alignItems: 'center',
alignSelf: 'center',
justifyContent: 'center',
flexDirection: 'column'
}} key="2">
<Image style={{marginBottom: 50, width: '35%', height: '35%'}}
source={require('../sele2.png')}/>
<Text style={{
fontFamily: 'mont',
color: '#000',
fontSize: 22,
letterSpacing: 0.5,
marginBottom: 12
}}>
With Sẹlẹ, you can
</Text>
<Text style={{
fontFamily: 'mont',
color: '#615D5D',
fontSize: 16,
letterSpacing: 1,
textAlign: 'center'
}}>
Hire services and buy and sell {'\n'} on Sẹlẹ
</Text>
</View>
<View style={{
backgroundColor: 'white',
alignItems: 'center',
alignSelf: 'center',
justifyContent: 'center',
flexDirection: 'column'
}} key="3">
<Text style={{
fontFamily: 'mont',
color: '#000',
fontSize: 22,
letterSpacing: 0.5,
marginBottom: 12
}}>
Select your School
</Text>
<Text style={{
fontFamily: 'mont',
color: '#615D5D',
fontSize: 16,
letterSpacing: 1,
textAlign: 'center'
}}>
You've made it this far {'\n'} select your school on the next page {'\n'} and let's get
started
</Text>
</View>
</ViewPagerAndroid>
<BoxShadow setting={shadowOpt}>
<View style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
flex: 1,
backgroundColor: 'white',
}}>
<TouchableNativeFeedback>
<Text style={{
fontSize: 18,
marginLeft: 25,
fontFamily: 'mont',
color: '#615D5D',
marginBottom: 10
}}>Skip</Text>
</TouchableNativeFeedback>
<View style={{flexDirection: 'row'}}><View
style={{marginRight: 14, alignSelf: 'center'}}>
<View style={this.state.no == "0" ? styles.selected : styles.unselected}>
</View>
</View>
<View style={{marginRight: 14}}>
<View
style={this.state.no == "1" ? styles.selected : styles.unselected}>
</View>
</View>
<View
style={this.state.no == "2" ? styles.selected : styles.unselected}>
</View></View>
<TouchableNativeFeedback onPress={this.update.bind(this)}>
<Text style={{
fontFamily: 'mont',
color: '#EFB879',
fontSize: 18,
marginRight: 25,
marginBottom: 10
}}>Next</Text>
</TouchableNativeFeedback>
</View>
</BoxShadow>
</View>
);
}
}
PARENT ELEMENT
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
timePassed: false,
};
}
render() {
return (
<View style={{flex: 1}}>
<StatusBar backgroundColor='#EE8F62' translucent={true} barStyle='light-content'/><On/>
</View>
);
}
}
We had similar problem for android, This is how we handle it was, may be this will help you to find a better solution.
You need to create a const and assign status bar size as a value.
const NAVBAR_HEIGHT = Platform.select({
ios: { marginTop: 0 },
android: { marginTop: window.height / 8 }
});
Then in the style section should add this line,
marginTop: NAVBAR_HEIGHT.marginTop
Like this,
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#E7EBF0",
marginTop: NAVBAR_HEIGHT.marginTop
}
});
This is how we create status bar for our application,
<View style={styles.container}>
{/* if ios status bar will be added to the screen */}
{Platform.OS === "ios" ? (
<Statusbar backgroundColor="#000000" barStyle="light-content" />
) : null}
</View>
I'm trying to have a list and then a button, but if I try to have a button and then a list like so:
return (
<View style={styles.mainContainer}>
<View>
<TouchableOpacity style={styles.Button} onPress={() => this.props.navigate(1)}>
{buttonText}
</TouchableOpacity>
</View>
<ScrollView>
<View style={{ height: 250, backgroundColor: 'red' }}>
</View>
<View style={{ height: 250, backgroundColor: 'blue' }}>
</View>
<View style={{ height: 250, backgroundColor: 'green' }}>
</View>
</ScrollView>
</View>
);
You see that green isn't being fully shown:
This doesn't happen if the button isn't there.
And if I move
<View>
<TouchableOpacity style={styles.Button} onPress={() => this.props.navigate(1)}>
{buttonText}
</TouchableOpacity>
</View>
to be after the ScrollView, I don't see the button.
So to sum it up, I want the button to be stickied to the bottom while having every element in the scroll view to be fully shown. How can it be done?
This is the style of the button BTW:
Button: {
borderWidth: 1,
borderRadius: 10,
padding: 10,
marginTop: 5,
margin: 3,
backgroundColor: '#4267b2',
borderColor: '#ffffff',
alignItems: 'center'
}
he proper way to achieve this is by using flex
this is an example for 1 : 10 flex body to header ratio
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.bodyContainer}>
<ScrollView >
<View style={{ height: 250, backgroundColor: 'red' }}>
</View>
<View style={{ height: 250, backgroundColor: 'blue' }}>
</View>
<View style={{ height: 250, backgroundColor: 'green' }}>
</View>
</ScrollView>
</View>
<View style={styles.headerContainer}>
<Button style={{ backgroundColor: 'blue', alignSelf:'center' }}
title="THIS IS A BUTTON" />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
mainContainer: {
flex: 1,
flexDirection: 'column'
},
headerContainer: {
flex: 1,
},
bodyContainer: {
flex: 10
}
});
You can stick your button view with a position: absolute, set its height and add a margin-bottom of this height on the scrollview.
Render
return (
<View style={styles.mainContainer}>
<ScrollView style={styles.list}>
<View style={{ height: 250, backgroundColor: 'red' }}>
</View>
<View style={{ height: 250, backgroundColor: 'blue' }}>
</View>
<View style={{ height: 250, backgroundColor: 'green' }}>
</View>
</ScrollView>
<View style={styles.fixedBottom}>
<TouchableOpacity style={styles.Button} onPress={() => this.props.navigate(1)}>
{buttonText}
</TouchableOpacity>
</View>
</View>
);
Style
...
fixedBottom: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: 50, // <== the height on your view containing the button
backgroundColor: 'gray',
},
list: {
alignSelf: 'stretch',
flex: 1,
marginBottom: 50, // <== here the margin to see the full list
},
...
Here is a live demo.
I have recently started using React-Native, and one of the main issues I have been struggling with is the layout design.
I have a "Card" like component that holds some information about movies. Components like Image and Text that are big goes out of the bounds of the card and does not respect the padding and margins provided.
Here is the JSX code:
<View style={[styles.container, styles.card]}>
<View style={{alignSelf: 'stretch', flexDirection: 'row', padding: 10}}>
<Image style={{height: 50, width: 50, borderRadius: 25}}
source={require('../../img/sarah-avatar.png.jpeg')}/>
<View style={{flexDirection: 'column', marginTop: 5, marginLeft: 15}}>
<Text style={styles.title}>{ this.state.movie.name }</Text>
<Text style={{color: 'gray'}}>{ moment(this.state.movie.releaseDate).format('DD/MM/YYYY') }</Text>
</View>
</View>
<View style={{alignItems: 'center'}}>
<!-- THIS IMAGE DOES NOT RESPECT THE CARD BOUNDS -->
<Image style={{height: 220, resizeMode: 'center'}}
source={require('../../img/advance-card-bttf.png')}/>
<View style={{flexWrap: 'wrap', marginTop: 10, marginBottom: 20}}>
<Text style={{color: 'gray', fontSize: 14}}>
{ this.state.movie.summary }
</Text>
</View>
</View>
<View style={{flexDirection: 'row', flex: 1}}>
<Button onPress={this.onLikeClick.bind(this)}>
<View style={{flexDirection: 'row'}}>
<Icon name="thumbs-up" size={18} color="#007aff" />
<Text style={{color: '#007aff', marginLeft: 5}}>{ this.state.movie.likes } Likes</Text>
</View>
</Button>
<View style={{width: 20}} />
<Button onPress={this.onCommentClick.bind(this)}>
<View style={{flexDirection: 'row'}}>
<Icon name="comment" size={18} color="#007aff" />
<Text style={{color: '#007aff', marginLeft: 5}}>{ this.state.movie.comments.length } Comments</Text>
</View>
</Button>
</View>
<Comments comments={this.state.movie.comments }
showComments={ this.state.showComments }
handler={this.handler} />
</View>
const styles = StyleSheet.create({
card: {
backgroundColor: "#fff",
borderRadius: 2,
shadowColor: "#000000",
shadowOpacity: 0.3,
shadowRadius: 1,
shadowOffset: {
height: 1,
width: 0.3,
},
padding: 10,
flexWrap: 'wrap'
},
container: {
flex: 1,
backgroundColor: '#F5FCFF',
margin: 5
},
title: {
fontSize: 20,
backgroundColor: 'transparent'
},
button: {
marginRight: 10
}
});
And the comments component JSX code:
<View style={{flexDirection: 'column'}}>
{
this.props.comments.map((comment, index) => {
return (
<View key={index} style={{flexDirection: 'row', marginTop: 20}}>
<Image style={{height: 50, width: 50, borderRadius: 25}}
source={require('../../img/avatar-ts-woody.png')}/>
<View style={{flexDirection: 'column', marginLeft: 15, marginTop: 3}}>
<Text style={{fontSize: 18}}>{ comment.name }</Text>
<!-- THE COMMENT CONTENT GOES OUTSIDE OF THE CARD -->
<Text style={{color: 'gray'}}>{ comment.content }</Text>
</View>
</View>
)
})
}
<TextInput placeholder="Leave a comment"
value={this.state.commentContent}
onChangeText={(text) => this.setState({commentContent : text})}
onSubmitEditing={this.onCommentEntered.bind(this)}
style={{height: 40, marginTop: 18}} />
</View>
Here you can see that the cover image seems like it's out of the card:
In here each comment might start correctly inside the card, but will go outside of the bounds when text is too long: