StyleSheet /CSS : Placing Components in React Native - css

I am working on a React Native App . The components are vertically center as I desire but are all aligned on the bottom and can't get them move up towards the top of the screen.
Please take a look at the layout screen (attached here).
How can I get them to move up top?
Please Note : The yellow color in the screen is to figure out how far the component's view stretches.
I could not figure out to adjust the 'yellow' view. The 'yellow' view is only suppose to consist the donut/menu button .
<View style={styles.container}>
<TouchableOpacity style={styles.drawerButton} onPress={() => this.props.navigation.openDrawer()} >
<Image
source={menu}
style={{width: 25, height: 25, marginLeft: 5}}
/>
</TouchableOpacity>
<TextInput style={styles.addText} placeholder="Add New Item"/>
<Button style = {styles.submitButton} title = "Add" />
<TouchableOpacity style = {styles.pictureButton} onPress = {this.setCameraWindow}>
<Text style={{fontSize:14}}> Take Picture </Text>
</TouchableOpacity>
{this.state.isCameraVisible ? this.takePicture() : null }
</View>
);
}
}
const styles = StyleSheet.create ({
container:{
flex:1,
flexDirection: 'column',
alignItems:'center',
flexGrow: 1,
justifyContent: 'center',
backgroundColor: 'blue'
},
drawerButton:{
flex:1,
flexDirection:'row',
alignSelf:'flex-start',
backgroundColor:'yellow'
},
menuButton: {
height:50,
flex:1,
flexDirection:'column',
alignSelf:'flex-start',
backgroundColor:'yellow',
},
addText:{
flex:1,
flexDirection:'column',
justifyContent:'center',
backgroundColor:'white',
borderBottomColor: 'black',
borderBottomWidth: 2,
alignSelf:'center',
maxHeight:50,
},
submitButton:{
justifyContent:'center'
},
pictureButton:{
backgroundColor:'white',
justifyContent: 'center',
},
});
Thank you

You have given flex:1 inside the style={styles.drawerButton}, thats why its taking up the entire space and other things are pushed to the end of the screen, please remove flex:1 from that, and also remove justifyContent: 'center' from container style. Then all the content will be moved to the top

These styles are what you need. Specifically:
overflow: hidden: Hides the yellow bit when the menu is collapsed.
position: absolute on the menu. This means the menu will appear in the same position regardless of the other content. Use top: 10px; left: 10px to control the actual positioning of the menu.
position: relative on the menu's parent (TouchableOpacity) element. This means the position: absolute of the menu will be relative to the parent element.
You may have to get rid of some of the other styles, at least temporarily, so you can see what is happening.

Related

How can i move my button upside as the text is

I want my text and button at the top of the screen, but both the elements have a few gaps between, so I added marginBottom to my text and then that text went to the top. But I want that button to be at the top so I added marginBottom to the button too, but now it's not going to the top.
How can I position it to the top? Where is the style issue coming from? Please, explain my mistake as well.
import { StyleSheet, Text, View, Button } from 'react-native';
export default function App() {
return (
<View style={styles.container}>
<Text style={{ margin: 16, marginBottom: '80%', backgroundColor: 'black' }}>Hello World</Text>
<View style={{ marginBottom: '80%' }}>
<Button title='Click me' />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
You're using alignItems: 'center' and justifyContent: 'center'.
These two properties combined make the contents of container be positioned in the center. alignItems:center vertically centers the contents, and justifyContent:center horizontally centers the contents.
Since Text is the upper child element, giving it a marginBottom will space it out from the View element containing the Button. If you compare your screen with and without that marginBottom on the Text you'll notice that the button also moves slightly to the bottom if you include the marginBottom on the Text.
So in short, your code is doing exactly what you tell it to do: container tries to position all children to its center.
To fix this, you might want to remove the container:{ alignItems:'center', } property. This will most likely push all your elements to the top of the container. After that you might simply use marginTop on your Text element to push the contents down.
Change both marginBottom %80 to "auto".
return (
<View style={styles.container}>
<Text
style={{ margin: 16, marginBottom: "auto", backgroundColor: "white" }}
>
Hello World
</Text>
<View style={{ marginBottom: "auto" }}>
<Button title="Click me" />
</View>
</View>
);

Center elements within a view

I have a form component with some TextInput subcomponents as follows:
const FormComponent = () => {
return (
<View style={StyleSheet.container}>
<TextInput1/>
<TextInput2/>
<TextInput3/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
});
When i try to center align the form components using the StyleSheet, nothing seems to happen. I.e., the text inputs remain at the top left corner of the screen.
However, if I apply the styling directly to the View as follows:
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
then the textinput components become center aligned on the screen.
I dont understand why this is happening and would like to keep the code clean by using stylesheets to do this if possible.
Use styles instead of StyleSheet , that's a typo
<View style={styles.container}>
<TextInput1/>
<TextInput2/>
<TextInput3/>
</View>

How to make an Animated image centered over a background without using fixed width and height?

I have an app which the user holds the screen (anywhere) to display an animation over the background. When the user releases the screen the animation stops and the background reappears. The problem is that I just don't know how to get the animated image to cover the entire screen.
At the moment it is a small rectangle in the centre of the screen.
Here's the code from the render and the style sheet:
render() {
return (
<ImageBackground source={background} style={styles.background}>
<TouchableOpacity style={styles.background}
onPressIn={this.onItemMouseDown}
onPressOut={this.onItemMouseUp}
>
</TouchableOpacity>
{this.state.isOn === true ? (
<View style={styles.background}>
<Text style={styles.timer}>{this.state.time}</Text>
{this.state.stateImages.map((item, index) => {
const opacity = this.opacity[index];
return (
<Animated.View
key={item.id}
style={[styles.anim, { animations: item, opacity}]}
>
<Image source={item.source} style={styles.animImage}/>
</Animated.View>
);
})}
</View>
) : null}
</ImageBackground>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
// justifyContent: "center",
// alignItems: 'center',
// position:"absolute"
},
background: {
flex: 1,
resizeMode: 'cover',
justifyContent:'center',
},
anim: {
flex: 1,
resizeMode :'cover',
alignSelf: "center",
position: 'absolute',
},
animImage:{
flex: 1,
resizeMode :'cover',
},
timer:{
alignSelf: "center",
zIndex: 100,
},
I've read:
Full screen background image in React Native app
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Setting component height to 100% in react-native
https://reactnative.dev/docs/layout-props
How do I center one image over another in React Native
Image resizing in React Native
But none of the suggestions work.
I think because this is using the <Animated.View/> which uses 2 styles styles.anim and styles.animImage it's not as straight forward as it seems.
How would I make this animated.view cover the screen without using fixed width and height (which wouldn't be compatible with different screen sizes)?
Any ideas?
T

Long text pushing other elements off screen with react native

I'm trying to build a layout in React-Native using Flexbox and I'm having trouble when the text is long. I want the layout to look like this:
However, when the blue text gets long, it pushes the date off of the right side of the screen like this:
I'm intentionally making the text stay on 1 line. What I want is for the blue text to expand as much as possible without making the text shrink. I'm new to RN and my work with CSS is very limited so I don't have much experience doing things like this. Here is my stylesheet code:
const styles = StyleSheet.create({
textContainer: {
flex: 1
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
title: {
fontSize: 25,
fontWeight: 'bold',
color: '#48BBEC'
},
subtitle: {
fontSize: 20,
color: '#656565'
},
dateContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'flex-end'
},
rowContainer: {
flexDirection: 'row',
padding: 10
}
});
And, finally, my layout code:
<TouchableHighlight
underlayColor='#dddddd'>
<View style={styles.rowContainer}>
<View cstyle={styles.textContainer}>
<Text numberOfLines={1} style={styles.title}>{rowData.name}</Text>
<Text style={styles.subtitle}>{rowData.teacher}</Text>
</View>
<View style={styles.dateContainer}>
<Text style={styles.subtitle}>{result}</Text>
</View>
<View style={styles.separator}/>
</View>
</TouchableHighlight>
Remove flex: 1 from dateContainer and add flexWrap: 'wrap' to textContainer.
textContainer: {
flex: 1,
flexWrap: 'wrap'
},
dateContainer: {
justifyContent: 'center',
alignItems: 'flex-end'
},
You'll have to set a width on the text component you wish to truncate (even if it's 100% of it's container which is the flexbox child I imagine), and also set numberOfLines prop to 1 (or however many lines you want to allow). See docs here on ellipsize mode and number of lines.
https://facebook.github.io/react-native/docs/text.html#ellipsizemode
Code example of what I explain above:
<View style={{display: 'flex', flexDirection: 'row', overflow: 'hidden'}}>
<View>
<Text
numberOfLines={1}
style={{width: '100%'}}>Text here that I want to truncate</Text>
<Text
numberOfLines={1}
style={{width:'100%'}}>Another line of text</Text>
</View>
<View style={{width: 120}}>
<Text>01/01/2017</Text>
</View>
</View>
If you're still having issues, it's possible you'll need to also use overflow: hidden in your text component style.
To solve your problem, remove the flex: 1 from the dateContainer and it won't go off the screen.
dateContainer: {
//flex: 1, line removed
justifyContent: 'center',
alignItems: 'flex-end'
},
Add the flex: 1 to your rowContainer as follows:
rowContainer: {
flex: 1, //Line added
flexDirection: 'row',
padding: 10
}
The solution to this is adding flexGrow: 0 to the element that is pushing others away.
I ended up solving this by measuring the parent view's width (with the onLayout callback) and then setting the width of the text's parent to the remainder.
This means having some state in the component, but it could be turned in to a general component with a left-side dynamic-sized component and a right-side static component.

Make an item stick to the bottom using flex in react-native

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)

Resources