please explain by what principle view sizes with element style are formed. I learned FlexBox however it breaks all my understanding.
code
export const App = () => {
return (
<SafeAreaView>
<View style={styles.container}>
<View style={styles.element}>
<View style={styles.element1}></View>
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
width: '100%',
height: '100%',
backgroundColor: 'green',
},
element: {
// width: '100%',
// height: '100%',
backgroundColor: 'yellow',
// top: '5%',
},
element1: {
width: '50%',
height: '80%',
backgroundColor: 'red',
},
});
You can use flex: 1 instead of
width: '100%', height: '100%',.
The Flex: 1 means the component (In case, the view) have a priority value of 1, wich means, if there'snt another component on the same view with flex: 1, the component will take the entire screen.
If, there's two components with flex: 1, each one will take 50% of the entire screen.
Here's the documentation: React native flexbox
Related
I have a flatlist containing between 1 - 4 children.
I want to give each child a width of 25% of the width of the container in this case the flatlist.
I have tried the following, but this results in the children having a width of 0.
How do i fix this.
<FlatList horizontal={true} style={welcome_style.favs_list} data={this.state.favorites} renderItem={(data: ListRenderItemInfo<GymSummary>) => {
return this.render_fav_gym(data.item);
}}></FlatList>
private render_fav_gym(data: GymSummary): JSX.Element {
return (
<TouchableOpacity style={{ width: "25%", aspectRatio: 1, padding: 10, backgroundColor: "blue", borderRadius: 14, marginHorizontal: 5 }}></TouchableOpacity>
);
}
const welcome_style = StyleSheet.create({
favs_list: {
width: "100%",
flex: 0,
backgroundColor: "red"
}
});
If the list has 1-4 items, and you always want all the items on screen, I would not recommend a FlatList. FlatLists are optimized for scrolling through long lists of items.
A simple View and the gap style should work for you. Here's one way you could do it. I added some Text inside the buttons, and you will probably have to change the styles to get the look you want.
<View style={welcome_style.favs_list}>
{this.state.favorites.map((_, index) => (
<TouchableOpacity style={welcome_style.button}>
<Text>{index + 1}</Text>
</TouchableOpacity>
)}
</View>
const welcome_style = StyleSheet.create({
favs_list: {
width: "100%",
backgroundColor: "red",
flexDirection: 'row',
gap: 5, // puts this much space between children
height: 48, // however tall you want the buttons to be here, plus padding
},
button: {
width: '25%',
aspectRatio: 1,
padding: 10,
backgroundColor: "blue",
borderRadius: 14,
justifyContent: 'center',
alignItems: 'center',
},
});
I'm trying to make a profile page ui, Where the profile data like name and bio container will stay 50% to the bottom of its top component which is cover picture right now.
So this is my React Native code,
<View style={styles.container}>
<View style={styles.coverContainer}>
</View>
<View style={styles.profileDataContainer}>
</View>
<View style={styles.intrestsContainer}>
</View>
<View style={styles.topicsContainer}>
</View>
</View>
And current styles,
const styles = StyleSheet.create({
container: {
},
coverContainer: {
height : 200,
backgroundColor : 'blue'
},
profileDataContainer: {
marginHorizontal : 30,
backgroundColor: 'white',
height : 200,
shadowColor: "#e5e5e5",
shadowOffset: {
width: 0,
height: 2
},
shadowRadius: 2,
shadowOpacity: 1.0,
elevation : 4,
marginTop : -100,
}
});
I have added marginTop : -100 to make it look like the structure I want.
So this is what it looks like right now after adding marginTop to -100, But that's not responsive, When I increase the size of that white block. It doesn't stay in center anymore.
Need Help on how to do that :(
You can do it using percentage instead of logical pixels like in this example :
const WindowHeight = Dimensions.get('window').height;
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.headerBackground}/>
<View style={styles.header}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: 'grey',
},
headerBackground: {
height: '30%',
width: '100%',
backgroundColor : 'blue'
},
header: {
height: '30%',
width: '80%',
marginTop: WindowHeight * -0.15,
backgroundColor : 'white'
}
});
In this example I set the height of the blue background and the white header to 30% of the window's height, and the marginTop to -15% of the window's height (I have to use Dimensions because if I use a percentage it'll be the percentage of the width...)
You can run my example on snack : https://snack.expo.io/Hyd7ChplX
You can nest your profileDataContainer in your coverContainer and place it absolute with top 50%. Hope you can convert it yourself to your React Native code...
Example:
.coverContainer{
height: 200px;
background-color: blue;
position: relative;
width: 100%;
}
.profileDataContainer {
position: absolute;
right: 10%;
left: 10%;
display: block;
top: 50%;
background-color: red;
height: 200px;
}
<div class="coverContainer"><div class="profileDataContainer"></div></div>
I would like to position my button to the bottom left of the screen. I have tried using bottom: 0 and left: 0, but that did not do anything. I researched a bit and discovered that I need to set the position: 'absolute'. However, when I do this, my button disappears completely.
How can I position by the button to the bottom left of the screen?
Here is my code:
import React, { Component } from 'react';
import { Button,Alert, TouchableOpacity,Image } from 'react-native'
import {
AppRegistry,
StyleSheet,
Text,
View,
} from 'react-native';
class Project extends Component {
render() {
return (
<View style={{backgroundColor: '#375D81', flex: 1}}>
<View style = {styles.container}>
<TouchableOpacity style = {styles.buttonText} onPress={() => { Alert.alert('You tapped the button!')}}>
<Text>
Button
</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
main: {
backgroundColor: 'blue'
},
container: {
alignItems: 'center',
},
buttonText: {
borderWidth: 1,
padding: 25,
borderColor: 'black',
backgroundColor: '#C4D7ED',
borderRadius: 15,
bottom: 0,
left: 0,
width: 100,
height: 100,
position: 'absolute'
}
});
AppRegistry.registerComponent('Project', () => Project);
You've forgotten to flex your container. Add: flex: 1 in there and you're all good:
container: {
alignItems: 'center',
flex: 1
},
You just need to set a width and height to your button then:
justifyContent: 'flex-end'
That's it
App.js
import React from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import styles from './styles';
class Example extends React.Component {
render() {
return (
<View style={styles.container}>
<TouchableOpacity style={styles.button}>
<Text style={styles.text}>Button</Text>
</TouchableOpacity>
</View>
);
}
}
export default Example;
styles.js
export default {
container: {
flex: 1,
justifyContent: 'flex-end',
},
button: {
justifyContent: 'center',
textAlign: 'center',
width: 70,
height: 40,
backgroundColor: 'green',
borderRadius: 10,
margin: 20,
},
text: {
color: 'white',
textAlign: 'center',
margin: 10,
},
};
You will get something like this:
See Expo Snack: snack.expo.io/#abranhe/button-left
React-native elements are "rendered" from the top left when defining their position absolutely. This means when you are defining bottom: 0 and left: 0, this is rendering the item where you want horizontally, but vertically it will be off the screen. switch to
position 'absolute',
bottom: 100,
left: 0,
This should be what you are looking for
I want to do width: 100% - 50 so I can add an icon which is 50 wide on the right hand side of it.
I have got width: 100% - 20% working by using react-native-extended-styles but I don't see why that is useful because you can do width: '80%'. I cannot get width: 100% - 50 working. Is there a way?
Trying to use the onLayout event to get the container width, then set the <autocomplete> to 100% - 50 of the container width but it isn't working.
let Location = (props) => {
let locationInputElement
const blur = () => {
locationInputElement.blur()
}
let inputContainerWidth
return (
<View style={styles.formItem}>
<View
onLayout={(event) => {
inputContainerWidth = event.nativeEvent.layout.width
console.log(inputContainerWidth)
}}
<Autocomplete
data={props.autocompleteResults.predictions}...
style={{
borderRadius: 8,
backgroundColor: 'red',
alignSelf: 'stretch',
paddingLeft: 10,
position: 'relative',
...styles.label,
...styles.labelHeight,
width: inputContainerWidth - 50
}}
/>
</View>
</View>
)
}
It does console.log 335 when it console.logs inputContainerWidth but the width of the <autocomplete> is 100% still.
I'd agree with Viktor, you should be able to achieve this using Flex Box.
Here's something I put together: https://snack.expo.io/B1jDKOhyb
You set the flexDirection of the formRow to row, and then the first child (the holder View for your AutoComplete component to flex: 1. This makes it fill all available space. The next child View is your icon holder. Which you can set to whatever value you want (in this case 50).
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<View style={styles.formRow}>
<View style={styles.formItem}>
// AutoComplete component goes here
</View>
<View style={styles.formIcon}>
// Icon goes here
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 100
},
formRow: {
flexDirection: 'row',
height: 50,
},
formItem: {
flex: 1,
backgroundColor: 'dodgerblue',
},
formIcon: {
width: 50,
backgroundColor: 'greenyellow',
},
});
This can easily be solved by using Dimensions.
import { Dimensions } from 'react-native';
const MyComponent = () => {
return <Text style={{height: Dimensions.get('window').height - 100}}></Text>
};
export default MyComponent;
You can do it without computing width. Use marginHorizontal: 50 with width:100 or flex:1.
Your code not working because, it's rendered then inputContainerWidth updated. To make it work, there should be another render with new inputContainerWidth. So you can't use a stateless component. Change Location to regular component and add inputContainerWidth to state.
Consider using useWindowDimensions from react-native (see docs):
import { useWindowDimensions } from 'react-native';
const { height, width } = useWindowDimensions();
I did margin: -50 in the parent and it worked for me
Suppose this is the layout:
<View style={styles.container}>
<View style={styles.titleWrapper}>
...
...
</View>
<View style={styles.inputWrapper}>
...
...
</View>
<View style={styles.footer}>
<TouchableOpacity>
<View style={styles.nextBtn}>
<Text style={styles.nextBtnText}>Next</Text>
</View>
</TouchableOpacity>
</View>
</View>
I want to make the view with the styles of footer to position at the bottom of the screen. I tried giving the alignSelf property to the footer, but instead of positioning at the bottom, it positions it to the right side of the screen. How can I make the footer item stick to the end? Thank you.
I would use the following approach:
<View style={styles.container}>
<View style={styles.contentContainer}> {/* <- Add this */}
<View style={styles.titleWrapper}>
...
</View>
<View style={styles.inputWrapper}>
...
</View>
</View>
<View style={styles.footer}>
...
</View>
</View>
var styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
titleWrapper: {
},
inputWrapper: {
},
contentContainer: {
flex: 1 // pushes the footer to the end of the screen
},
footer: {
height: 100
}
});
This way the styles of titleWrapper and inputWrapper can be updated without breaking the layout of your app and the components themselves are easier to re-use :)
In React Native, the default value of flexDirection is column (unlike in CSS, where it is row).
Hence, in flexDirection: 'column' the cross-axis is horizontal and alignSelf works left/right.
To pin your footer to the bottom, apply justifyContent: 'space-between' to the container
for me the answer was to create a container view for the elements, then for the style.
bottomContainer: {
flex: 1,
justifyContent: 'flex-end',
}
Absolutely position is another way to fix footer, just like:
footer: {
position: 'absolute',
height: 40,
left: 0,
top: WINDOW_HEIGHT - 40,
width: WINDOW_WIDTH,
}
To fix a View to the bottom, simply use: marginTop: 'auto' .
This worked for me after searching like an hour on the net. I tried experimenting and it worked!
Consider a screen structure
<View style={styles.container}>
<View style={styles.body}> ... </View>
<View style={styles.footer}>...</View>
</View>
You can do it cleanly using Flexbox approach utilizing flex-grow.
const Styles = StyleSheet.create({
container:{
flexDirection: 'column', // inner items will be added vertically
flexGrow: 1, // all the available vertical space will be occupied by it
justifyContent: 'space-between' // will create the gutter between body and footer
},
})
Note: In case of nested elements, you have to ensure that the parent container has enough height to work with when using flexGrow. Set backgroundColor on parents and child to debug.
To do this you can use the Stylesheet element position: 'absolute'.
/*This is an Example to Align a View at the Bottom of Screen in React Native */
import React, { Component } from 'react';
import { StyleSheet, View, Text } from 'react-native';
export default class App extends Component {
render() {
return (
<View style={styles.containerMain}>
<Text> Main Content Here</Text>
<View style={styles.bottomView}>
<Text style={styles.textStyle}>Bottom View</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
containerMain: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
bottomView: {
width: '100%',
height: 50,
backgroundColor: '#EE5407',
justifyContent: 'center',
alignItems: 'center',
position: 'absolute', //Here is the trick
bottom: 0, //Here is the trick
},
textStyle: {
color: '#fff',
fontSize: 18,
},
});
You can use this style:
row: {
flexDirection: 'row',
height: 50,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute', //Here is the trick
bottom: 0,
}
embed other content in a scrollview
<View style={styles.container}>
<ScrollView> {/* <- Add this */}
<View style={styles.titleWrapper}>
...
</View>
<View style={styles.inputWrapper}>
...
</View>
</ScrollView>
<View style={styles.footer}>
...
</View>
</View>
In react native, there are some properties like position: 'absolute', bottom: 0, which you will want to give to your button view
Quick example in essence, based on #David's answer:
<View style={{flex: 1}}>
<View style={{flex: 1}}>
<Text>Main content</Text>
</View>
<Text>Footer</Text>
</View>
import React from 'react'
import { View, StyleSheet } from 'react-native'
function moveToBottom(component) {
return (
<View style={styles.container}>
{component}
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-end',
marginBottom: 36
}
})
export default moveToBottom
Now in our screen, we just need to import:
import moveToBottom from 'library/utils/moveToBottom'
and wrap our button:
{
moveToBottom(
<ImageButton
style={styles.button}
title={strings.onboarding.welcome.button}
onPress={() => {
this.props.navigation.navigate('Term')
}} />
)
}
I tested it and I approve it's the best option to respect the layout without having fixed things to bottom, which is not possible if you use react-native-web in addition of react-native, because people resize and elements overlap on each over.
Source: https://medium.com/react-native-training/position-element-at-the-bottom-of-the-screen-using-flexbox-in-react-native-a00b3790ca42
I have a case in which I have to show a image in the bottom like this, as you can see the sky-blue image is not poped-up with keyboard.
so for this I have created a functional component for image in bottom.
import React, { useEffect, useState } from "react";
import { Keyboard, View, Image } from "react-native";
export const BottomImage = (props) => {
const [shouldShow, showImage] = useState(true);
useEffect(() => {
Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
Keyboard.addListener("keyboardDidHide", _keyboardDidHide);
return () => {
Keyboard.removeListener("keyboardDidShow", _keyboardDidShow);
Keyboard.removeListener("keyboardDidHide", _keyboardDidHide);
};
}, []);
let _keyboardDidShow = () => {
showImage(false)
}
let _keyboardDidHide = () => {
showImage(true)
}
return (<ViewToRender show={shouldShow} src={props.image} />)
}
function ViewToRender(props) {
return props.show ? <Image style={{ position: 'absolute', bottom: 0 }} source={props.src} /> : <View />
}
and to use this Bottom image you have to pass your image to it like :
<BottomImage image={AppImage.signupbottom} />
This can be a bit tricky given that parent components can still affect the height of children with 'absolute' styles, I also tried doing "bottom: 0, height: 'auto'" like with normal HTML/CSS, but it didn't work out well, down the line I'll probably create a general component which makes sure the view can fit into the screen size. End result of view with contents
<View> component parameters
style={{
position: 'absolute',
left: 0,
padding: ContainerPadding,
top: TopOffset,
width: ScreenWidth
}}
onLayout={(event) => {
var {x, y, width, height} = event.nativeEvent.layout; // get the View's dimensions after 1st render
SetTopOffset(ScreenHeight - height - HeaderHeight); // Set 'top' to: screen size - height (of view) - parent top offset (optional if no parent offset)
}}
With useState:
const [TopOffset, SetTopOffset] = useState<number>(0); // Controls 'top' of screen
HeaderHeight is the height which is added to all my page components, you can remove this variable if you do not have any top spacing. (currently set to 64 default and this variable is updated based on device)
ScreenWidth & ScreenHeight are calculated here: export const ScreenWidth = Dimensions.get('screen').width; export const ScreenHeight = Dimensions.get('screen').height;
ContainerPadding is a general padding number used across my project (currently set to 12.5)