How can I stretch my subview across 100% width of its parent, minus 20px margin on each side? In other words, I need it to fill the width of the parent, with 20px open on each side.
I know in React-Native I can use width: '80%' to make my subview's width relative to that of its parent, but then it's not always precisely 20px on the sides. I also know that I can use alignSelf: 'stretch', however that is not working for me - it has unexpected / unreliable results. I don't want to use Dimensions, as the parent will not always be the device's screen, so Dimensions.get('window').width is inadequate for this problem.
What other options do I have?
Use nested View. You can try here: https://snack.expo.io/#vasylnahuliak/stackoverflow-67989491
import React, { useState } from 'react';
import { Text, View, StyleSheet } from 'react-native';
const App = () => {
return (
<View style={styles.container}>
<View style={styles.child}>
<Text style={styles.childText}>child</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 24,
backgroundColor: 'tomato',
},
child: {
height: 150,
backgroundColor: 'navy',
},
childText: {
color: 'white',
},
});
export default App;
Related
I have the following code:
import React from "react";
import { StyleSheet, View } from "react-native";
function App() {
return (
<View style={styles.container}>
<View style={styles.row_1}>
<View style={styles.square} />
</View>
<View style={styles.row_2}>
<View style={styles.square} />
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1
},
row_1: {
flex: 1,
backgroundColor: "red",
alignItems: "center",
padding: 10
},
row_2: {
flex: 5,
backgroundColor: "blue",
alignItems: "center",
//flexDirection: 'row',
padding: 10
},
square: {
flex: 1,
aspectRatio: 1,
backgroundColor: "white"
}
});
export default App;
I'm trying to insert a white square in the center of the rows, without knowing a priori the height and the width of the rows. In the example, on a 4:3 phone, the first row is larger then higher and the second row higher than larger.
The square in the first row is correct, but the square in the second row exceeds the margins. I can manually set flexDirection: 'row' in the row_2 to achieve the expected result, but just because I know the container is higher than larger. How can I achieve the same result, without specifing the flex direction?
Sandbox here
Not exactly sure what the final result you're looking for, but for something responsive like this I would consider using a dynamic unit on width/height properties on your squares like viewport units -- vw, vh, vmin, vmax.
Alternatively you can get the viewport dimensions in pixels:
//e.g.
document.documentElement.clientWidth
document.documentElement.clientHeight
//or including scrollbar:
window.innerWidth
window.innerWidth
Then use a css styles library like emotion use the viewport dimensions in your styles.
Hello i was following along a tutorial and it was going fine, until the tutor used react-native-paper ProgressBar in his project, I wrote exactly same but it was not visible, I found the documentation and plugged it, still not visible.
What am i Doing wrong?
import {View, StyleSheet, Text} from 'react-native' ;
import { ProgressBar, Colors } from 'react-native-paper';
import CountDown from '../comps/CountDown.js' ;
const Timer = ({task}) => {
return (
<View style={styles.container}>
<CountDown />
<Text style={styles.text}> Focusing On:</Text>
<Text style={[styles.text, styles.textBold]}> {task} </Text>
<ProgressBar progress={0.4} color='#5E84E2' style={styles.progress}/>
</View>
) ;
}
const styles = StyleSheet.create({
container : {
flex: 1,
width: 100,
alignItems: 'center' ,
justifyContent: 'center',
paddingTop: 40,
},
text: {
// flex: 0.5,
color: 'white'
},
textBold: {
fontWeight: 'bold' ,
// borderColor: 'white',
// borderWidth: 1,
// borderStyle: 'solid' ,
},
progress: {
height: 10,
// borderColor: 'white',
// borderWidth: 1,
// borderStyle: 'solid' ,
}
}) ;
export default Timer ;
Also, If i were to Give border to the progressBar, it appears but keep on increasing in width even when width is more than the viewport width. I dont know what is happening
The problem here is when you use alignItems the children components need to have a fixed width, your progress bar doesnet have a fixed width.
You will have to provide a with in the styles.
progress: {
height: 10,
width:50
}
Based on documentation default value for width is
auto (default value) React Native calculates the width/height for the
element based on its content, whether that is other children, text, or
an image.
Better have a value for width which will solve your issue.
A responsive solution
Currently ProgressBar doesn't support the flex styling system.
If someone is also looking to make the width equal to 100% if it's parrent component, there is a good recommended solution provided here.
Just set the width to undefined:
progress: {
height: 10,
width:undefined
}
e.g.
<View style={{ flex: 2, other flex styles }}>
<ProgressBar progress={0.5} color="white" style={{ height: 5, width: undefined }} />
</View>
I have a ScrollView which has two children, and I want each of the children to be the full height of the available space on the screen, meaning view 1 fills the whole screen, and then I scroll down to start to see part of slide 2. If I scroll all the way down, I should only see slide 2.
I can't figure out how to get this to happen. What is currently happening is that each child is filling half of the available vertical space, so I don't get any scrolling.
Any ideas how I can achieve this layout? Thanks!
Current UI: https://i.imgur.com/fZdbUSo.png
Desired UI:
Before scrolling: https://i.imgur.com/EsQpVMm.png
After scrolling all the way down: https://i.imgur.com/PfXQyfI.png
Code:
import React from "react";
import {
StyleSheet,
ScrollView,
Text,
View
} from "react-native";
const Home = () => {return (
<ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollViewContainer}>
<View style={styles.slide1}>
<Text>this should be full height</Text>
</View>
<View style={styles.slide2}>
<Text>this should also be full height, but only be visible once i scroll down</Text>
</View>
</ScrollView>
)};
const styles = StyleSheet.create({
slide1: {
justifyContent: "center",
flexGrow: 1,
backgroundColor: "blue"
},
slide2: {
justifyContent: "center",
flexGrow: 1,
backgroundColor: "green"
},
scrollView: {
flex: 1
},
scrollViewContainer: {
flexGrow: 1
}
});
export default Home;
I'm not sure if there is a way to do what you are trying to accomplish with flex layout.
I would suggest getting the height of the visible container - either the height of the window or calculate it some other way, possibly using onLayout of the containing view and getting the height there, and then you have to set the height of your two inner views using that. You may need to wrap your scrollView in a View and use its onLayout function instead of the one for the scrollView.
To do the second part of your question, scrollView has a snapToInterval prop you can set.
Let me know how that goes for you or if you need code examples.
I was able to get this to work by using onLayout() as Doug Watkins suggested and updating the height of the children to match the height of the parent ScrollView:
import React, { Component } from "react";
import {
StyleSheet,
ScrollView,
Text,
View,
LayoutChangeEvent
} from "react-native";
class Home extends Component {
state = {
slideHeight: 0
};
updateHeight = (e: LayoutChangeEvent) => {
this.setState({ slideHeight: e.nativeEvent.layout.height });
};
styles = StyleSheet.create({
slide1: {
justifyContent: "center",
backgroundColor: "blue"
},
slide2: {
justifyContent: "center",
backgroundColor: "green"
},
scrollView: {
flex: 1
},
scrollViewContainer: {
flexGrow: 1
}
});
render () {
return (
<ScrollView style={this.styles.scrollView} contentContainerStyle={this.styles.scrollViewContainer} onLayout={this.updateHeight}>
<View style={[this.styles.slide1, {height: this.state.slideHeight}]}>
<Text>this should be full height</Text>
</View>
<View style={[this.styles.slide2, {height: this.state.slideHeight}]}>
<Text>this should also be full height, but only be visible once i scroll down</Text>
</View>
</ScrollView>
)
}
};
export default Home;
You can use react-native-swiper to achieve this functionality. Set your data or component in swiper slides and set props horizontal={false} to get vertical sliding.
So, first install react-native-swiper using this command:
npm i react-native-swiper --save
Now, import and use it as following :
import React from "react";
import { StyleSheet, ScrollView, Text, View } from "react-native";
import Swiper from "react-native-swiper";
const { width } = Dimensions.get("window");
const { height } = Dimensions.get("window");
const Home = () => {
return (
<Swiper style={{}} height={height} horizontal={false}>
<View style={styles.slide1}>
<Text>this should be full height</Text>
</View>
<View style={styles.slide2}>
<Text>
this should also be full height, but only be visible once i scroll
down
</Text>
</View>
</Swiper>
);
};
const styles = StyleSheet.create({
slide1: {
justifyContent: "center",
flexGrow: 1,
backgroundColor: "blue",
},
slide2: {
justifyContent: "center",
flexGrow: 1,
backgroundColor: "green",
},
scrollView: {
flex: 1,
},
scrollViewContainer: {
flexGrow: 1,
},
});
export default Home;
I know this may not perfect solution, but it work around. Swiper originally made up with scrollview, so there may be not problem.
I'm trying to achieve the following effect in React Native:
The image has a button in the corner. The button is always within the corner of the image regardless of the image's size or aspect ratio, and no part of the image is clipped (it is always scaled down to fit fully within a box).
The trouble I'm having in React Native is that the Image component's size doesn't always match the scaled-down size of the image. If I fix the image's height to 300, set flex 1 to make the image's width expand to fill its contents, and the image is a portrait, the Image component with being the full width of the container, but the image within the component will have a width of much less. Therefore, the typical approach for having a view overlay another view doesn't work as I would like it to- my overlay also covers the padding around the image, and the button (anchored to the corner) appears outside of the image.
Here's what it looks like in React Native:
The X is a placeholder for the button. It is set to anchor to the top-left of a View that's a child of the same View that the Image is a child of. The backgroundColor of the image is set to green to demonstrate how the width of the Image component is different from the width of the picture that's inside the component.
The goal is that the X would be inside of the image regardless of its aspect ratio. I think I could do something based on grabbing the image's dimension and scaling the height and width of the Image component, but that sounds complicated and fragile. Is this possible in a responsive way with styling?
Demonstration code:
<View
style={{
marginLeft: 7,
marginRight: 7,
backgroundColor: 'blue',
}}
>
<View
style={{
height: 300,
flex: 1,
}}
>
<Image
source={imageSource}
style={{
flex: 1,
height: undefined,
width: undefined,
backgroundColor: 'green',
}}
resizeMode="contain"
/>
</View>
<View
style={{
position: 'absolute',
right: 5,
top: 5,
backgroundColor: 'transparent',
}}
>
<Text style={{ color: 'white' }}>X</Text>
</View>
</View>
From React-native v0.50.0 <Image> with nested content is no longer supported. Use <ImageBackground> instead.
<ImageBackground
source={imageSource}
>
<View>
<Text>×</Text>
</View>
</ImageBackground>
Here is how I accomplished that:
import React, { Component } from "react";
import { View, Image, StyleSheet } from "react-native";
import { Ionicons } from "#expo/vector-icons";
class MyCard extends Component {
render() {
return (
<View style={styles.container}>
<Image
resizeMode="cover"
style={styles.cover}
source={{ uri: "https://picsum.photos/700" }}
/>
<Ionicons style={styles.close} name="ios-close-circle" size={25} />
</View>
);
}
}
export default MyCard;
const styles = StyleSheet.create({
container: {
margin: 5,
width: 160,
height: 200
},
cover: {
flex: 1,
borderRadius: 5
},
close: {
margin: 5,
position: "absolute",
top: 0,
left: 0,
width: 25,
height: 25,
color: "tomato"
}
});
Here is how it looks like :
Updated 29 Jun 2019
Now(react-native#0.60-RC) there's a specific component to wrap elements as image background, ImageBackground. Go for the official documentation.
Following Gist has been changed.
Original Answer
We can use <Image/> display images, and we can use it as a background-image hack.
try this
<Image
source={imageSource}
>
<View>
<Text>×</Text>
</View>
</Image>
this gist is a full demo for your need.
or you can see it live at expo:
<div data-snack-id="B1SsJ7m2b" data-snack-platform="ios" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#fafafa;border:1px solid rgba(0,0,0,.16);border-radius:4px;height:505px;width:100%"></div>
<script async src="https://snack.expo.io/embed.js"></script>
So the way you do this, as #ObooChin mentioned, is to use Image.getSize() to get the actual size of the image, then generate a height and width based on a) the maximum space you want the image to take up, and b) the aspect ratio of the actual image's dimensions.
import React, { Component } from 'react';
import {
StyleSheet,
Image,
View,
Dimensions,
} from 'react-native';
export default class FlexibleThumbnail extends Component {
constructor(props) {
super(props)
this.state = {
imageWidth: 0,
imageHeight: 0,
source: null,
}
}
componentWillMount() {
this._updateState(this.props)
}
componentWillReceiveProps(nextProps) {
this._updateState(nextProps)
}
_updateState(props) {
const {source} = props;
const height = props.maxHeight;
const width = props.maxWidth || Dimensions.get('window').width;
const imageUri = source.uri;
Image.getSize(imageUri, (iw, ih) => {
const {imageWidth, imageHeight} = /* some function that takes max height, width and image height, width and outputs actual dimensions image should be */
this.setState({
imageWidth,
imageHeight,
source,
});
});
}
render() {
const {source, height, width, imageWidth, imageHeight} = this.state;
const overlay = (/* JSX for your overlay here */);
// only display once the source is set in response to getSize() finishing
if (source) {
return (
<View style={{width: imageWidth, height: imageHeight}} >
<Image
style={[ imageStyle, {width: imageWidth, height: imageHeight} ]}
resizeMode="contain"
source={source}
/>
<View style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
backgroundColor: 'transparent',
}}>
{overlay}
</View>
</View>
);
}
return (
<View />
)
}
}
The attempt is still a bit rough, but I'm working on turning this into a library where you can specify max height, width, and the overlay you want to use, and it handles the rest: https://github.com/nudgeyourself/react-native-flexible-thumbnail.
I'm building a React Native app for the first time using an iOS Simulator with XCode, and it seems that my elements on the bottom of the screen are being cut off, as if the screen can only scroll so far. I've tried changing the overall container View's height, but it doesn't change how far down I can scroll. I've also tried removing the styling, as well as changing View to ScrollView, but neither helped. Another strange thing is that I need to add paddingTop, otherwise, the first Text is displayed over the time in the simulator
Here's the code:
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Fields from './components/Fields.js'
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Calculator</Text>
<Text>1099 Calculator</Text>
<Fields />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
paddingTop: 50
},
});
Here's the top of the screen without the padding
And here's how it looks cut off
This is an issue I also had. To fix it just wrap your container with another view without padding but with flex. So like this:
export default class App extends React.Component {
render() {
return (
<View style={styles.main}>
<View style={styles.container}>
<Text>Calculator</Text>
<Text>1099 Calculator</Text>
<Fields />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
paddingTop: 50
},
main: {
flex: 1
}
});
I was facing the same problem, it turned out to be the padding that I added to my ScrollView. Maybe it is not the solution, it is simply to discard ideas..