Related
Alright, so this has got me busy for quite a few hours already. I am trying to create a login screen where the main components are rendered on the bottom, with the logo in the remaining space. This is kind of what I would like to achieve:
To support my textinputs, I use KeyboardAwareScrollView, as it works better for me as opposed to KeyboardAvoidingView. My code currently looks like this (I plan on using a background image with a 50% color overlay rather than the red background, so the ImageBackground has to stay in place too):
<ImageBackground
source={require('./assets/img/background-clouds.png')}
resizeMode="cover"
style={styles.backgroundImage}>
<View style={styles.backgroundOverlay} />
<View style={styles.dummyView}>
<Text>elloha</Text>
</View>
<Image
source={require('./assets/img/logo.png')}
style={styles.backgroundLogo}
resizeMode="contain"
/>
<KeyboardAwareScrollView
keyboardShouldPersistTaps="always"
keyboardOpeningTime={0}
alwaysBounceHorizontal={false}
alwaysBounceVertical={false}
contentInsetAdjustmentBehavior="automatic"
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
automaticallyAdjustContentInsets={false}
extraScrollHeight={30}
enableOnAndroid>
<StatusBar
backgroundColor="transparent"
barStyle="light-content"
hidden={false}
translucent
/>
<TouchableWithoutFeedback
onPress={Keyboard.dismiss}
accessible={false}>
<View style={styles.content}>
<View style={styles.backgroundContainer}>
<SafeAreaView style={{ flex: 0 }} />
<View style={styles.loginContainer}>
<View style={styles.loginScreen}>
// textinputs and buttons go here
</View>
<SafeAreaView style={{ backgroundColor: 'white' }} />
</View>
<View
style={{
backgroundColor: 'white',
height: Dimensions.get('window').height,
position: 'absolute',
width: Dimensions.get('window').width,
top: Dimensions.get('window').height,
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
</ImageBackground>
Relevant styles:
const styles = StyleSheet.create({
container: {
backgroundColor: "white",
},
content: {
flex: 1,
},
backgroundImage: {
flex: 1,
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
},
backgroundContainer: {
justifyContent: "flex-end",
flex: 1,
width: Dimensions.get("window").width,
height: Dimensions.get("window").height,
},
backgroundOverlay: {
backgroundColor: "red",
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
},
logoContainer: {
top: "10%",
width: "100%",
},
backgroundLogo: {
alignSelf: "center",
position: "absolute",
width: 126,
height: 96,
},
dummyView: {
backgroundColor: "red",
flex: 1,
},
loginContainer: {
borderTopEndRadius: 30,
borderTopStartRadius: 30,
width: "100%",
backgroundColor: "white",
height: 500,
alignItems: "center",
paddingTop: Dimensions.get("window").width * 0.1,
},
loginScreen: {
width: "80%",
backgroundColor: "white",
},
});
This yields the following result:
I can get it done by adding top: 160 to the backgroundLogo style, but that's a fixed value. I want it to be always in the center of the available space, but I'm unable to add a view between the background and the loginContainer, as all the logic for the keyboard and such is handled in between.
Is there a way to achieve what I want? Ideally, I should also be able to check the available height, and only show the logo if there is enough space (e.g. available height > 100, otherwise don't show logo).
Important:
I want the logo to stay fixed, so if the keyboard is shown, the logo should not move up. The loginContainer should go "over" the logo
EDIT:
Wrap Image inside a View with this style and give the loginContainer style height: '70%' :
...
<View style={styles.dummyView}>
<Text>elloha</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'center',
height: '30%',
position: 'absolute',
width: '100%',
}}>
<Image
source={require('./assets/img/logo.png')}
style={styles.backgroundLogo}
resizeMode="contain"
/>
</View>
<KeyboardAwareScrollView
keyboardShouldPersistTaps="always"
...
...
loginContainer: {
borderTopEndRadius: 30,
borderTopStartRadius: 30,
width: '100%',
backgroundColor: 'orange',
height: '70%',
alignItems: 'center',
paddingTop: Dimensions.get('window').width * 0.1,
},
...
hie! I think using Dimension to get a specific screen's height and deciding it has 70% of the screen covered via form sheet and rest is free for a logo to be in and we can ask it to be down a little using rest of height's 50% as margin-top ( the image will be in the center of that image )
here is a SNACK LINK to see your example working with my suggested solution.
here is the draft code:
import * as React from 'react';
import { Text, View, StyleSheet, Dimensions, ImageBackground,TextInput,
Image, TouchableWithoutFeedback, Keyboard, SafeAreaView, StatusBar} from 'react-native';
import Constants from 'expo-constants';
import {KeyboardAwareScrollView} from 'react-native-keyboard-aware-scroll-view';
export default function App() {
return (
<ImageBackground
source={{
uri: 'https://cdn.pixabay.com/photo/2021/08/23/18/37/tea-6568547_960_720.jpg',
}}
resizeMode="cover"
style={styles.backgroundImage}>
<View style={styles.backgroundOverlay} />
<Image
source={require('./assets/snack-icon.png')}
style={styles.backgroundLogo}
resizeMode="contain"
/>
<KeyboardAwareScrollView
keyboardShouldPersistTaps="always"
keyboardOpeningTime={0}
alwaysBounceHorizontal={false}
alwaysBounceVertical={false}
contentInsetAdjustmentBehavior="automatic"
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
automaticallyAdjustContentInsets={false}
extraScrollHeight={30}
enableOnAndroid>
<StatusBar
backgroundColor="transparent"
barStyle="light-content"
hidden={false}
translucent
/>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={styles.content}>
<View style={styles.backgroundContainer}>
<SafeAreaView style={{flex: 0}} />
<View style={styles.loginContainer}>
<View style={styles.loginScreen}>
<TextInput value={'email'} style={{borderBottomWidth:1, padding:10}}/>
<TextInput value={'password'} style={{borderBottomWidth:1, padding:10}}/>
</View>
<SafeAreaView style={{backgroundColor: 'white'}} />
</View>
<View
style={{
backgroundColor: 'white',
height: Dimensions.get('window').height,
position: 'absolute',
width: Dimensions.get('window').width,
top: Dimensions.get('window').height,
}}
/>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAwareScrollView>
</ImageBackground>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
},
content: {
flex: 1,
},
backgroundImage: {
flex: 1,
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
backgroundContainer: {
justifyContent: 'flex-end',
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
backgroundOverlay: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
logoContainer: {
top: '10%',
width: '100%',
},
backgroundLogo: {
alignSelf: 'center',
position: 'absolute',
marginTop: Dimensions.get('window').height/7, // top for rest of the screen to push logo down
width: 126,
height: 96,
},
dummyView: {
backgroundColor: 'red',
flex: 1,
},
loginContainer: {
borderTopEndRadius: 30,
borderTopStartRadius: 30,
width: '100%',
backgroundColor: 'white',
height: Dimensions.get('window').height/1.5, //container of form height
alignItems: 'center',
paddingTop: Dimensions.get('window').width * 0.1,
},
loginScreen: {
width: '80%',
backgroundColor: 'white',
},
});
Here is the output of code on android/ios:
there's a gap above the Image inside the View. Im trying to make it fit the Image inside my View
My Output
What i want to achieve
Here is my code
import React from 'react';
import {View, Text, StyleSheet, Image, TouchableOpacity} from 'react-native';
import THEME from '../../constants/theme.constants';
import SmallButton from './SmallButton';
import default_pp from '../../assets/default_pp.png';
import thumbnail_post from '../../assets/thumbnail_post.png';
function PostServiceProvider({user, ratings, rate, description}) {
return (
<View style={styles.container}>
<View style={styles.headerPost}>
<Image source={default_pp} style={styles.profilePicContainer} />
<TouchableOpacity style={{marginLeft: 10}}>
<Text style={styles.priceText}>{user}</Text>
</TouchableOpacity>
<View style={{position: 'absolute', right: 5}}>
<Text>{ratings}</Text>
</View>
</View>
<View style={styles.cardContainer}>
<Image source={thumbnail_post} style={styles.thumbnailImg} />
<View style={styles.rateContainer}>
<Text style={styles.text}>Daily Rate: </Text>
<Text style={styles.priceText}>Php 400</Text>
<View style={styles.buttonContainer}>
<SmallButton label="Hire" />
</View>
</View>
<View style={styles.br} />
<View style={{padding: 20}}>
<Text numberOfLines={2} style={styles.text}>
{description}
</Text>
</View>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
width: '100%',
borderRadius: 50,
borderColor: 'transparent',
marginTop: 20,
},
headerPost: {flexDirection: 'row', alignItems: 'center'},
profilePicContainer: {
height: 40,
width: 40,
borderRadius: 50,
borderWidth: 1,
},
cardContainer: {
width: '100%',
flex: 1,
borderColor: 'rgba(0,0,0,0.2)',
borderRadius: 10,
borderWidth: 0.5,
marginTop: 10,
alignItems: 'center',
},
thumbnailImg: {width: '100%', height: 300},
rateContainer: {
flexDirection: 'row',
alignItems: 'center',
width: '100%',
padding: 20,
},
buttonContainer: {width: '22%', position: 'absolute', right: 15},
br: {height: 1, width: '90%', backgroundColor: 'rgba(0,0,0,0.1)'},
text: {color: THEME.FONT_FADE},
priceText: {fontWeight: 'bold', fontSize: 17},
});
export default PostServiceProvider;
Add resizeMode="cover" props in your image component like this :
... your previous code
<View style={styles.cardContainer}>
<Image source={thumbnail_post} style={styles.thumbnailImg} resizeMode="cover" />
... your rest of code
I am attempting to align an image within and ImageBackground component though no matter what I do, it doesn't appear to follow the flexbox rules.
Below is the desired result
But I am currently getting
Below is the code I have used
render () {
console.log(this.props.data.attributes.main_image_url)
return (
<TouchableOpacity onPress={this._handlePress} style={styles.container}>
<ImageBackground source={{uri: "https:"+this.props.data.attributes.main_image_url}} style={styles.background}>
<View style={styles.logoContainer}>
<Image
// defaultSource={require('../Images/logo-placeholder.png')}
source={{uri: "https:"+this.props.data.attributes.logo_url}}
resizeMode="contain"
style={styles.logo}
/>
</View>
</ImageBackground>
</TouchableOpacity>
)
}
Styles
container: {
marginBottom: Metrics.doubleSection,
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'stretch',
},
background: {
height: Metrics.screenWidth / 2,
width: Metrics.screenWidth-(Metrics.baseMargin*2),
margin: Metrics.baseMargin
},
logoContainer: {
backgroundColor: Colors.coal,
justifyContent: 'flex-end',
alignItems: 'left',
left: Metrics.doubleBaseMargin,
height: Metrics.images.xl,
width: Metrics.images.xl
},
logo: {
height: Metrics.images.xl,
width: Metrics.images.xl
}
Change your logoContainer style as below set position:'absolute' and set bottom:negative value:
logoContainer: {
backgroundColor: Colors.coal,
justifyContent: 'flex-end',
alignItems: 'flex-start',
position:'absolute', // set position to absolute
bottom:-10, //give relative value here
left: Metrics.doubleBaseMargin,
height: Metrics.images.xl,
width: Metrics.images.xl
},
I am new in react native and i want a view like attached image. I have no idea that how can achieved this..?
I have tried this:
app.js
<View style={styles.navSectionStyle}>
<TouchableOpacity
style={styles.SubmitButtonStyle}
activeOpacity = { .5 }
onPress={ this.ButtonClickCheckFunction }>
<Text style={styles.TextStyle}> Page 1 </Text>
</TouchableOpacity>
<View style={styles.BackStyle}>
<Text style={styles.TextStyle}> Test</Text>
</View>
</View>
style.js
SubmitButtonStyle: {
width:'70%',
height: 80 ,
justifyContent: 'center',
alignItems: 'center',
paddingTop:15,
paddingBottom:15,
backgroundColor:'#fff',
borderRadius:10,
borderWidth: 2,
borderColor: '#fff'
},
BackStyle:{
marginTop:10,
marginLeft:15,
position: 'absolute',
width: 30,
borderRadius:10,
borderWidth: 1,
borderColor: '#F53BBB',
backgroundColor: '#F53BBB',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000000',
shadowOffset: {
width: 2,
height: 3
},
shadowRadius: 4,
shadowOpacity: 1.0
},
TextStyle:{
justifyContent: 'center',
alignItems: 'center'
}
Here's a basic template for the View
Get the Width of the screens using Dimensions api.
Mark the views as position: absolute and get position them according to the screen width.
15 % from both sides, 10% for overlapping view and 60% for the main view
render() {
return (
<View style={{flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'red'}}>
<View style={{width: Dimensions.get('window').width * 0.6,
height: 100,
backgroundColor: 'blue'}}/>
<View style={{position: 'absolute',
width: Dimensions.get('window').width * 0.1,
height: 80,
backgroundColor: 'black',
left: Dimensions.get('window').width * 0.15,
zIndex: 1}} />
<View style={{position: 'absolute',
width: Dimensions.get('window').width * 0.1,
height: 80,
backgroundColor: 'black',
right: Dimensions.get('window').width * 0.15,
zIndex: 1}} />
</View>
)
}
I'm trying to make a layout as per below with React Native.
How do I specify the position of B relative to A?
With iOS Interface Builder and autoconstraints, this can very explicitly be done and is a breeze. It's not so obvious how one might achieve this with React Native.
Add the following style to the "floating" view:
position: 'absolute'
You may also need to add a top and left value for positioning.
The above solutions were not working for me. I solved it by creating a View with the same background colour as the parent and added negative margin to move the image upwards.
<ScrollView style={{ backgroundColor: 'blue' }}>
<View
style={{
width: '95%',
paddingLeft: '5%',
marginTop: 80,
height: 800,
}}>
<View style={{ backgroundColor: 'white' }}>
<Thumbnail square large source={{uri: uri}} style={{ marginTop: -30 }}/>
<Text>Some Text</Text>
</View>
</View>
</ScrollView>
and I got the following result.
You can use zIndex for placing a view on top of another. It works like the CSS z-index property - components with a larger zIndex will render on top.
You can refer: Layout Props
Snippet:
<ScrollView>
<StatusBar backgroundColor="black" barStyle="light-content" />
<Image style={styles.headerImage} source={{ uri: "http://www.artwallpaperhi.com/thumbnails/detail/20140814/cityscapes%20buildings%20hong%20kong_www.artwallpaperhi.com_18.jpg" }}>
<View style={styles.back}>
<TouchableOpacity>
<Icons name="arrow-back" size={25} color="#ffffff" />
</TouchableOpacity>
</View>
<Image style={styles.subHeaderImage} borderRadius={55} source={{ uri: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Albert_Einstein_1947.jpg/220px-Albert_Einstein_1947.jpg" }} />
</Image>
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white"
},
headerImage: {
height: height(150),
width: deviceWidth
},
subHeaderImage: {
height: 110,
width: 110,
marginTop: height(35),
marginLeft: width(25),
borderColor: "white",
borderWidth: 2,
zIndex: 5
},
You can use this OverlayContainer. The trick is to use absolute with 100% size. Check below an example:
// #flow
import React from 'react'
import { View, StyleSheet } from 'react-native'
type Props = {
behind: React.Component,
front: React.Component,
under: React.Component
}
// Show something on top of other
export default class OverlayContainer extends React.Component<Props> {
render() {
const { behind, front, under } = this.props
return (
<View style={styles.container}>
<View style={styles.center}>
<View style={styles.behind}>
{behind}
</View>
{front}
</View>
{under}
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
height: '100%',
justifyContent: 'center',
},
center: {
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
},
behind: {
alignItems: 'center',
justifyContent: 'center',
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%'
}
})
import React, {Component} from 'react';
import {StyleSheet, View} from 'react-native';
export default class App extends Component {
render() {
return (
<View>// you need to wrap the two Views an another View
<View style={styles.box1}></View>
<View style={styles.box2}></View>
</View>
);
}
}
const styles = StyleSheet.create({
box1:{
height:100,
width:100,
backgroundColor:'red'
},
box2:{
height:100,
width:100,
backgroundColor:'green',
position: 'absolute',
top:10,
left:30
},
});
You can use react-native-view-overflow plugin for placing a view on top of another. It works like the CSS z-index property.
import ViewOverflow from 'react-native-view-overflow';
<ViewOverflow />
<View style={[styles2.cardBox, { marginTop: 50 }]}>
<View style={[styles2.cardItem]} >
<Text style={styles2.cardHeader}>userList</Text>
</View>
<View style={[styles2.cardContent]}>
<Text style={styles2.text}>overflow: "visible"</Text>
</View>
<View style={[styles2.cardItemFooter]} >
<Text style={styles2.cardTextFooter}>...</Text>
</View>
</View>
</ViewOverflow>
const styles2 = StyleSheet.create({
cardBox: {
borderLeftWidth: 0,
borderTopWidth: 0,
backgroundColor: "transparent",
borderWidth: 1,
borderColor: "#d0d0d0",
width: '94%',
alignSelf: 'center',
height: 200,
position: "relative",
borderRadius: 15,
overflow: "visible" // doesn't do anything
},
cardContent: {
textAlign: "right",
backgroundColor: "transparent",
marginTop: 15,
alignSelf: 'flex-end',
padding: 5,
},
cardHeader: {
color: '#fff',
fontFamily: 'Vazir',
fontSize: 12
},
cardItem: {
backgroundColor: "#3c4252",
borderRadius: 3,
position: "absolute",
top: -10,
right: -5,
width: 50,
height: 20,
paddingRight: 5,
},
})
The easiest way to achieve this is with a negative margin.
const deviceWidth = RN.Dimensions.get('window').width
a: {
alignItems: 'center',
backgroundColor: 'blue',
width: deviceWidth,
},
b: {
marginTop: -16,
marginStart: 20,
},
You can use elevation property for Android if you don't mind the shadow.
{
elevation:1
}
Try this:
style = {{position: 'absolute', bottom: 20, left: 20, elevation: 100}}
Based on the example above i've created a component which stacks all childeren on top of each other. You could even nest OverlayContainers inside OverlayContainers.
Usage:
<OverlayContainer>
<View style={{backgroundColor:'red', width:150, height: 150}}></View>
<View style={{backgroundColor:'yellow', width:50, height: 50}}></View>
<Text>Just some text</Text>
</OverlayContainer>
Output:
import React, { FC, PropsWithChildren } from 'react'
import { StyleSheet, View } from 'react-native'
export const OverlayContainer: FC<PropsWithChildren<unknown>> = (props) => {
return (
<View style={styles.container}>
{props.children.map((child, index) => (
<View style={styles.child} key={index}>
{child}
</View>
))}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: '100%',
},
child: {
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%'
}
})
<SafeAreaView style={{ flex: 1 }} >
<View style={{ height: Dimensions.get('window').height / 2, backgroundColor: 'blue', justifyContent: 'center' }}>
<Text style={{ fontSize: 25, alignSelf: 'center' }} >A</Text>
<View style={{ justifyContent: 'center', height: 100, width: 100, backgroundColor: 'yellow', position: 'absolute', left: 20, top: Dimensions.get('window').height / 2 - 70 }}>
<Text style={{ fontSize: 22, alignSelf: 'center' }} >B</Text>
</View>
</View>
</SafeAreaView>