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.
Related
I was making an app and added text on HomeScreen. I am getting the text in Web but am not able to get it in Android and IOS. This is the Code:
render(){
let pulse = <Image source={require('../pulse.png')} style={styles.pulse} />
return (
<SafeAreaView style={styles.container}>
<View>
<StatusBar barStyle="dark-content" />
<View style={{ flex: 0.4, resizeMode: 'contain' }}>
<Text style={styles.appName}>L{pulse}blood</Text>
</View>
</View>
</SafeAreaView>
}
These are the styles
container: {
flex: 1,
backgroundColor: 'white',
},
appName: {
alignSelf: 'center',
fontFamily: 'oswald',
fontSize: 50,
color: 'black',
height: '100%'
}
You need to remove the flex prop of the child view. Change
<View style={{ flex: 0.4, resizeMode: 'contain' }}>
<Text style={styles.appName}>L{pulse}blood</Text>
</View>
to
<View style={{ resizeMode: 'contain' }}>
<Text style={styles.appName}>L{pulse}blood</Text>
</View>
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 use react-native-vector-icons.
Click the input
Keyboard goes up
When pressing the icon, icon doesn't trigger, the keyboard will go down first rather than the onpress method icon triggers while having the keyboard is on
Expected Result would like a live chat, while keyboard is up submit icon will always trigger.
I tried to enwrap it in scrollview with keyboardshouldpersisttaps and it doesn't work.
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : null}
style={{ flex: 1 }}
keyboardVerticalOffset={64}
>
<View style={styles.slide} key={i}>
<TouchableHighlight
style={styles.video}
onPress={this.handleDoubleTap}
onLongPress={this.handlePlayAndPause}
>
<ViewportAwareVideo
key={i}
source={{ uri: firstVideoUri }}
shouldPlay={this.state.shouldPlay}
isLooping
preTriggerRatio={-0.4} // default is 0
retainOnceInViewport={false} // default is false
style={styles.video}
onPlaybackStatusUpdate={this._onPlaybackStatusUpdate}
progressUpdateIntervalMillis={1000}
resizeMode='contain'
innerRef={ref => (this._videoRef = ref)}
onViewportEnter={() => {
this.setState({ shouldPlay: true });
}}
onViewportLeave={() => {
this.setState({ shouldPlay: false });
}}
/>
</TouchableHighlight>
{this.state.shouldPlay ? null : (
<TouchableHighlight
onPress={this.handlePlayAndPause}
style={styles.pauseBtn}
>
<IconComponent name='play-circle' size={50} color='black' />
</TouchableHighlight>
)}
<CopilotStep
name='swipeUp'
text='Swipe up to view next video'
order={5}
>
<WalkthroughableView style={styles.topSection}>
<Text style={styles.imageHeadingText}>{product.name}</Text>
{product.short_description === '' ? null : (
<HTML
html={product.short_description}
imagesMaxWidth={Dimensions.get('window').width}
containerStyle={styles.imageDescText}
baseFontStyle={styles.htmlStyle}
/>
)}
<CopilotStep
name='swipeRight'
text='Swipe right to view product details'
order={6}
>
<WalkthroughableView
style={{
flexDirection: 'row',
flexWrap: 'wrap',
paddingHorizontal: 20,
paddingVertical: 5
}}
>
{product.tags.map((value, index) => {
return (
<Text key={index} style={styles.tagText}>
{value.name.charAt(0) === '#'
? value.name
: '#' + value.name}
</Text>
);
})}
</WalkthroughableView>
</CopilotStep>
{product.total_sales > 0 ? (
<View
style={{
flexDirection: 'row',
flexWrap: 'wrap',
paddingHorizontal: 20,
paddingVertical: 5
}}
>
<Text style={styles.totalSales}>
{product.total_sales +
(product.total_sales > 100
? '+ bought'
: ' bought in the last 24 hours')}
</Text>
</View>
) : null}
</WalkthroughableView>
</CopilotStep>
<View style={styles.bottomSection}>
<View style={{ flex: 1, justifyContent: 'flex-end', height: 30 }}>
{this.state.messages.map((message, index) => (
<React.Fragment key={index}>
<View
key={index}
style={{
flexDirection: 'row',
alignItems: 'center',
marginHorizontal: 5,
marginVertical: 5,
paddingLeft: 10,
height: 20
}}
>
<Image
style={{ width: 20, height: 20, borderRadius: 20 / 2 }}
source={{ uri: 'https://picsum.photos/20/20' }}
/>
<Text
style={{
fontFamily: Constants.fontHeader,
marginHorizontal: 5,
color: '#007AFF'
}}
>
{message.user.name}
</Text>
<Text
style={{
fontFamily: Constants.fontHeader,
marginHorizontal: 5,
color: 'white'
}}
>
{message.text}
</Text>
</View>
</React.Fragment>
))}
</View>
<CopilotStep
name='chatOnFeed'
text='Click here to chat on video feed'
order={7}
>
<WalkthroughableTextInput
style={{
// position: 'absolute',
// bottom: 0,
// left: 0,
fontFamily: Constants.fontFamily,
marginBottom: 110,
marginHorizontal: 5,
marginVertical: 5,
paddingRight: 35,
paddingLeft: 20,
height: 35,
width: width - 60,
backgroundColor: 'white',
borderRadius: 25
}}
onChangeText={messageText => this.setState({ messageText })}
value={this.state.messageText}
placeholder='Share your thoughts'
placeholderTextColor='#9B9B9B'
/>
</CopilotStep>
<IconComponent
style={{ position: 'absolute', bottom: 115, right: 10 }}
name='arrow-right'
size={25}
color='black'
onPress={product => this.sendMessage(product)}
/>
</View>
<View style={styles.iconBar}>
<CopilotStep
name='productDetail'
text='Click here to got to product details'
order={8}
>
<WalkthroughableText>
<IconComponent
style={styles.iconBarIcon}
name='shopping-cart'
size={iconSize}
color='white'
onPress={() => {
this.props.onViewProductScreen({ product });
this.setState({ shouldPlay: false });
}}
/>
</WalkthroughableText>
</CopilotStep>
<Text style={styles.iconBarText}>Shop</Text>
<CopilotStep
name='like'
text='Click here to like this product'
order={9}
>
<WalkthroughableText>
<Entypo
style={styles.iconBarIcon}
name='heart'
size={30}
color={this.state.isInWishList ? 'red' : 'white'}
onPress={() => {
this.state.isInWishList
? this.props.removeWishListItem(product)
: this.props.addWishListItem(product);
this.setState(prevState => ({
isInWishList: !prevState.isInWishList
}));
}}
/>
</WalkthroughableText>
</CopilotStep>
<Text style={styles.iconBarText}>Like</Text>
<CopilotStep
name='share'
text='Click here to share this product'
order={10}
>
<WalkthroughableText>
<IconComponent
style={styles.iconBarIcon}
name='share'
size={iconSize}
color='white'
onPress={this.onShare}
/>
</WalkthroughableText>
</CopilotStep>
<Text style={styles.iconBarText}>Share</Text>
<CopilotStep
name='fullChat'
text='Click here to view full chat'
order={11}
>
<WalkthroughableText>
<IconComponent
style={[styles.iconBarIcon, { paddingTop: 4 }]}
name='message-circle'
size={iconSize}
color='white'
onPress={product => this.sendChat(product)}
/>
</WalkthroughableText>
</CopilotStep>
</View>
</View>
</KeyboardAvoidingView>
);
You can see my problem is on IconComponent after the WalkthroughableTextInput
If you are using NativeBase component use it like this
<Content keyboardShouldPersistTaps={'handled'}>
...
</Content>
For ReactNative <ScrollView/>
<ScrollView keyboardShouldPersistTaps={'handled'}>
...
</ScrollView>
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:
I tried both absolute and flex dimensions, the View just sets the Button at standard dimensions (approx 50px). The height is stretched, but the width can't possibly change.
class Calculator extends Component {
constructor() {
super();
this.state = { "lastClicked" : "None" };
this.buttonPress = this.buttonPress.bind(this);
}
createButton(id) {
return (
<Button
title={id}
onPress={() => this.buttonPress(id)}
/>
);
}
buttonPress(id) {
this.setState({ "lastClicked" : id });
}
render() {
return (
<View style={{alignContent: "stretch"}}>
<View style={{width: 500}}>
<Text style={{fontSize: 30}}>This is a JavaScript Calculator</Text>
<Text style={{fontSize: 20}}>You clicked: {this.state.lastClicked}</Text>
</View>
<View style={{width: 500}}>
<View style={{ width: 500, height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch"}}>
{this.createButton("4")}
{this.createButton("5")}
{this.createButton("6")}
</View>
<View style={{ width: 500, height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch"}}>
{this.createButton("4")}
{this.createButton("5")}
{this.createButton("6")}
</View>
<View style={{ width: 500, height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch"}}>
{this.createButton("1")}
{this.createButton("2")}
{this.createButton("3")}
</View>
</View>
</View>
);
}
}
}
Can you see the issue? I am new to React-Native and the issue already consumed 3 hours of my life.
You can replace the createButton() method with something like that:
createButton(id) {
return (
<TouchableWithoutFeedback onPress={() => this.buttonPress(id)}>
<View style={{flex: 3, flexDirection:'column', justifyContent:'center', alignItems:'center'}}>
<Text style={{fontSize:30}}>{id}</Text>
</View>
</TouchableWithoutFeedback>
);
}
I wrapped a <View> with style flex: 3 for setting the width to a third of the screen size. flexDirection:'column' and justifyContent:'center' will center the number in the middle of the View. The View is wrapped in a TouchableWithoutFeedback so that we can bind the onPress event.
For your grid of numbers, you wrapped then in <View style={{width: 500}}>. This is not always the size of the screen. It's easy to get the screen width using Dimensions.
At the start of your file, you can declare something like:
const screenWidth = Dimensions.get('window');
And then replace <View style={{width: 500}}> by <View style={{width: screenWidth}}>.
Overall, your new code should look like:
import React, { Component } from 'react';
import { Text, View, Dimensions, TouchableWithoutFeedback } from 'react-native';
const screenWidth = Dimensions.get('window');
export default class App extends Component {
constructor() {
super();
this.state = { "lastClicked" : "None" };
this.buttonPress = this.buttonPress.bind(this);
}
createButton(id) {
return (
<TouchableWithoutFeedback onPress={() => this.buttonPress(id)}>
<View style={{flex: 3, justifyContent:'center', alignItems:'center'}}>
<Text style={{fontSize:30}}>{id}</Text>
</View>
</TouchableWithoutFeedback>
);
}
buttonPress(id) {
this.setState({ "lastClicked" : id });
}
render() {
return (
<View style={{alignContent: "stretch"}}>
<View style={{width: screenWidth}}>
<Text style={{fontSize: 30}}>This is a JavaScript Calculator</Text>
<Text style={{fontSize: 20}}>You clicked: {this.state.lastClicked}</Text>
</View>
<View style={{width:screenWidth}}>
<View style={{height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch",}}>
{this.createButton("7")}
{this.createButton("8")}
{this.createButton("9")}
</View>
<View style={{height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch"}}>
{this.createButton("4")}
{this.createButton("5")}
{this.createButton("6")}
</View>
<View style={{height: 150, flexDirection : "row", alignItems: "stretch", alignContent: "stretch", alignSelf: "stretch"}}>
{this.createButton("1")}
{this.createButton("2")}
{this.createButton("3")}
</View>
</View>
</View>
);
}
}
Demo on snack.expo.io