I build react native app and I'm trying to make 2 items per row with equal width in flatList.
what I get it's 2 items per row but not equal width.
const itemProduct = props => {
return (
<Button style={style.container} onPress={props.onPress}>
<View style={style.imageContainer}>
<LoadingImage resizeMode='contain' resizeMethod='resize' source={props.Image ? { uri: 'https://www.saramashkim.co.il/'+props.Image } : require('../../assets/images/default_no_image.png')} style={style.image} />
</View>
<View style={style.textContainer}>
<Text style={style.productName}>vodka</Text>
<View style={style.specialOffer}>
{props.inPromotion ? <Text>special offer</Text> : null}
</View>
<View style={style.bottomLine}>
<Text style={style.costText}>lorem ipos</Text><View style={style.bottomLine}>{props.discount ? <Text style={style.Price}>lorem ipos{parseFloat(Math.round(props.price_per_case * 100) / 100).toFixed(2)}</Text> : null}<Text style={style.discountPrice}>dollar{parseFloat(Math.round(props.price_per_case * ((100 - props.discount) / 100) * 100) / 100).toFixed(2)}</Text></View>
</View>
</View>
</Button>
)
}
style
const style = StyleSheet.create({
container : {
backgroundColor: colors.dark_red,
paddingTop:calcSize(10),
margin:calcSize(10),
minWidth:(width-15)/2,
},
imageContainer:{
flex:1,
alignItems:'center'
},
textContainer:{
flex:2,
//backgroundColor:'red',
paddingHorizontal:calcSize(30)
},
image:{
width: calcSize(175),
height: calcSize(175)
},
brandName:{
color:'#7c7c7c',
fontFamily:'Poppins-Light',
fontSize:calcSize(25),
marginBottom:calcSize(15)
},
productName:{
color: colors.black,
fontFamily:'Poppins-Regular',
fontSize:calcSize(20),
width:calcSize(300)
},
flatList
<View style={style.list}>
<FlatList
refreshing={this.state.refreshing}
numColumns = {2}
onRefresh={this.onListRefresh}
data={products}
keyExtractor={(o, i) => i.toString()}
renderItem={this.renderProductListItem}
ListFooterComponent={() => {
if ((this.state.products.length < this.state.totalFound || this.state.loading) && !this.state.refreshing)
return <Spinner style={style.loading} />
return <View style={style.loading} />
}}
ItemSeparatorComponent={() => <View style={style.separator} />} />
</View>
</View>
list style
list:{
flex:1,
flexDirection:'row',
paddingHorizontal: calcSize(30),
}
photo
I want to make 2 item per row with equal width, also into the photo as you can see the text in left not equal to the right so it's issue.
Use flexbox with flexbasis and flex grow try this
<View style={{flex: 1, flexDirection: 'row', flexWrap: 'wrap', flexGrow: 0}}>
<View style={{flexBasis: '50%', height: 50, backgroundColor: 'powderblue'}} />
<View style={{flexBasis: '50%', height: 50, backgroundColor: 'skyblue'}} />
<View style={{flexBasis: '50%', height: 50, backgroundColor: 'steelblue'}} />
<View style={{flexBasis: '50%', height: 50, backgroundColor: 'blue'}} />
</View>
Tested in my android device.
Live demo is over here! Give this a try
If any issue comment that down below
Right now you've exclusively set the width of the child element as minWidth:(width-15)/2, the margin as margin:calcSize(10),
padding for the main container as paddingHorizontal: calcSize(30) which is causing the issue .
If you want to go the minWidth way, then you need to calculate the spacing correctly
minWidth = (screenWidth - (paddingHorizontal * 2) - (margin * 2) - (separatorWidth)) / 2 = (screenWidth - 80) / 2
Since there is no separatorWidth mentioned in the question
paddingHorizontal: Setting paddingHorizontal is like setting both of paddingLeft and paddingRight.
margin:
Setting margin has the same effect as setting each of marginTop, marginLeft, marginBottom, and marginRight.
list:{
flex:1,
paddingHorizontal: calcSize(30),
},
container : {
flex: 1, //<== Either add a height or a flex
backgroundColor: 'darkred',
paddingTop:calcSize(10),
margin:calcSize(10),
minWidth:(width-80)/2,
},
Better approach would be to use height and flex , so that width can be scaled accordingly.
Here's a snack demo
Related
I started to learn React Native two days ago and I am wondering why the backgroundColor "yellow" and "green" are not working. My whole screen is white. I thought that, because I used 3 times a flex: 0.5, that the screen is devided in 3 parts with different backgroundColors. What is the problem here? The dividing in 3 parts seems to work, because the text is, image and button are on the top 1/3, but the second 1/3 and third 1/3 are white.
Here is the code:
console.log("App executed");
return(
<View style={styles.viewStyles}>
<Text style={styles.textStyles} onPress={() => console.log("Text clicked")}>Beispielfüroben</Text>
<TouchableOpacity onPress={() => console.log("Bild clicked")}>
<Image source={require("./assets/books.jpg")} style={styles.logoStyles}></Image>
</TouchableOpacity>
<Button
style={styles.buttonStyles}
title= "Beispielbutton"
onPress={() => Alert.alert("Hallo", "I bims 1 clown",[{text: "hallo", onPress: () => console.log("Hallo clicked")}, {text: "no"}])}
></Button>
<View style={{backgroundColor: "yellow", flex: 0.5}}/>
<View style={{backgroundColor: "green", flex: 0.5}}/>
</View>
)
};
const styles = StyleSheet.create({
viewStyles: {
backgroundColor: "white",
flex: 0.5,
alignItems: 'center',
justifyContent: 'center'
},
Can you help me?
Thanks a lot :)
The issue here is that a View, which does not contain any children, won't fill any space unless we are telling it to do so.
The flex property will only
define how your items are going to “fill” over the available space along your main axis. Space will be divided according to each element's flex property.
Hence, let your Views occupy 100% of the available height and width. If the flex property is equal for both views, then the available space will be filled equally.
If you want to let the text, button and touchable opacity, occupy 1/3 of the screen, then you need to put them inside the same parent view with a flex of 1 as well and provide the same height and width percentage (100 in both cases).
export default function App() {
return (
<SafeAreaView style={styles.viewStyles}>
<View style={{ width: '100%', height: '100%', flex: 1, alignItems: 'center' }}>
<Text onPress={() => console.log('Text clicked')}>Beispielfüroben</Text>
<TouchableOpacity onPress={() => console.log('Bild clicked')}>
<Image
source={require('./assets/snack-icon.png')}
style={styles.logoStyles}></Image>
</TouchableOpacity>
<Button
title="Beispielbutton"
onPress={() =>
Alert.alert('Hallo', 'I bims 1 clown', [
{ text: 'hallo', onPress: () => console.log('Hallo clicked') },
{ text: 'no' },
])
}></Button>
</View>
<View
style={{
backgroundColor: 'yellow',
flex: 1,
width: '100%',
height: '100%',
}}
/>
<View
style={{
backgroundColor: 'green',
flex: 1,
width: '100%',
height: '100%',
}}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
viewStyles: {
backgroundColor: 'white',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
logoStyles: {
width: 24,
height: 24,
},
});
The result is as follows
Here is a snack.
I have the above ScrollViews( highlighted in yellow color ) each one with maximum 6 items. When I try to scroll it to the end, I can't scroll 100%. Part of the last item will not view. See the following screenshot.
The highlighted red area is not able to see or scroll.
Following is the component,
const Item = ({title, data}: any) => (
<View style={styles.itemRoot}>
<View style={styles.item}>
<View style={styles.itemLeft}>
{/* left inner container */}
</View>
<View style={styles.itemRightRoot}>
<View style={styles.itemRightTitle}>
<Text>TITLE</Text>
</View>
<View style={styles.itemRight}>
{/* area which render each ScrollViews */}
{title.items.map((item: any, index: number) => {
return (
<ScrollView horizontal>
{item.map((child, index) => {
return (
<View
key={index}
style={{
width: isTablet()
? (Dimensions.get('screen').width - 80) / 6
: (Dimensions.get('screen').width - 64) / 3,
marginRight: 8,
marginBottom: 8,
}}>
<SectionInnerItem />
</View>
);
})}
</ScrollView>
);
})}
</View>
</View>
</View>
{/* separator */}
<View
style={{
height: 2,
marginTop: 24,
marginBottom: 24,
backgroundColor: '#36363D',
width: Dimensions.get('screen').width - 64,
}}
/>
</View>
);
And this is the stylesheet,
const styles = StyleSheet.create({
itemRoot: {
marginLeft: 24,
marginRight: 16,
},
item: {
display: 'flex',
flexDirection: 'row',
},
itemRightRoot: {
display: 'flex',
flexDirection: 'column',
},
itemRightTitle: {
marginLeft: 24,
marginRight: 16,
},
itemRight: {
marginLeft: 24,
marginRight: 16,
paddingTop: 16,
paddingBottom: 16,
},
itemLeft: {},
});
What am I doing wrong here? Also, is there a way I can just use one single ScrollView but with a maximum of 6 items on each row and the rest of the items in the next row?
Please Give flex: 1 for the itemRightRoot. The problem is that, itemRightRoot doesn't know the width of the rest of the screen with the square on the left side of the screen.
itemRightRoot: {
flex: 1,
display: 'flex',
flexDirection: 'column',
},
Hii i want to display images horizontally having 2 columns for this i am using scrollview but i dont know how to do that , i am using following code
code to fetch api
componentDidMount(){
return fetch('https://www.json-generator.com/api/json/get/ccLAsEcOSq?indent=1')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson.book_array,
}, function(){
});
})
.catch((error) =>{
console.error(error);
});
}
code for render
render() {
if (this.state.isLoading === true) {
return <ActivityIndicator color={'red'} />;
}
return (
<View style={styles.container}>
<ScrollView horizontal={true}>
{this.state.dataSource.map(item => this.renderItem(item))}
</ScrollView>
</View>
);
}
}
code for renderItem
renderItem(item) {
return (
<View style={{ margin: 5 }}>
<View style={{
backgroundColor: 'red',
width: 150,
height: 150,
marginBottom: 1,
}}>
<Image style={{ width: 150,height: 150}}
source={{uri: item.image}}/>
</View>
<View style={{
backgroundColor: 'red',
width: 150,
height: 150,
marginBottom: 1,
}}>
<Image style={{ width: 150,height: 150}}
source={{uri: item.image}}/>
</View>
</View>
);}
Instead of ScrollView try FlatList which provides numColumns props which lets you allow to use columns as per your choice.
Instead of this,
<ScrollView horizontal={true}>
{this.state.dataSource.map(item => this.renderItem(item))}
</ScrollView>
Use this,
<FlatList
data={this.state.dataSource}
numColumns={2}
renderItem={this.renderItem}
/>
For more details of FlatList see Official Docs Here
Try flex-direction property:
renderItem(item) {
return (
<View style={{ margin: 5, flex: 1, flexDirection: "row", justifyContent: "space-around" }} >
<View style={{ backgroundColor: "red", width: 150, height: 150, marginBottom: 1 }} >
<Image style={{ width: 150, height: 150 }} source={{ uri: item.image }} />
</View>
<View style={{ backgroundColor: "red", width: 150, height: 150, marginBottom: 1 }} >
<Image style={{ width: 150, height: 150 }} source={{ uri: item.image }} />
</View>
</View>
);}
modify your ScrollView component Like this:
<ScrollView horizontal={true} contentContainerStyle={{height:300, flexWrap:'wrap'}}>
{this.state.dataSource.map(item => this.renderItem(item))}
</ScrollView>
Use flat list inside scroll view like this
<FlatList
horizontal={true}
data={this.state.dataSource}
renderItem={({ item }) => (this.renderItem({item}))}
/>
rearrange dataSource like this
array1 = [obj1,obj2,obj3,obj4,obj5,obj6,obj7,
array2=[[obj1,obj2],[obj3,obj4],[obj5,obj6],[obj7,obj8]]
and then render item with 2 rows.
didn't find any other way
To create view as you required need to implement your custom logic. In render function call a intermediate function to get columns in two row:
render() {
if (this.state.isLoading === true) {
return <ActivityIndicator color={'red'} />;
}
return (
<View style={styles.container}>
<ScrollView horizontal={true}>
{this.renderHorizantol(this.state.dataSource)}
</ScrollView>
</View>
);
In renderHorizantol function need to set up logic for even or odd rows, i am implementing this on index of dataSource Array:
renderHorizantol = (dataSource) =>{
let view = []
for(let i=0 ; i < data.length ; i = i+2)
{
let subView = this.renderItem(dataSource[i],dataSource[i+1])
view.push(subView)
}
return view
}
In renderItem function pass two element to draw upper and lower row contents:
renderItem(item1,item2) {
let image1 = item1["imageUrl"]
let image2 = item2 ? item2["imageUrl"] : null
return (
<View style={{ margin: 5 }}>
<View style={{
backgroundColor: 'red',
width: 150,
height: 150,
marginBottom: 1,
}}>
<Image style={{ width: 150,height: 150}}
source={{uri: image1}}/>
</View>
<View style={{
backgroundColor: 'red',
width: 150,
height: 150,
marginBottom: 1,
}}>
<Image style={{ width: 150,height: 150}}
source={{uri: image2}}/>
</View>
</View>
);}
I build react native app and I use with scrollView for header with list of text horizontal.
The issue is that the height of the scroll view takes half size of the screen. Even after declared it as a style, it still stays as it is.
screen with the scrollView
<View style={Style.container} >
{this.props.ExtendedNavigationStore.HeaderTitle ? <BackHeader header={this.props.ExtendedNavigationStore.HeaderTitle} onPressBack={this.goBack} /> : <Header openDrawer={this.openDrawer} />}
<ScrollView contentContainerStyle={{flexGrow:1}} style={Style.scrollableView} horizontal showsHorizontalScrollIndicator={false}>
{this.renderScrollableHeader()}
</ScrollView>
<Routes /> /* stack with dashboard screen */
</View>
</Drawer>
)
}
styles
import {StyleSheet} from 'react-native'
import {calcSize} from '../../utils'
const Styles = StyleSheet.create({
container : {
flex:1,
backgroundColor:"#e9e7e8"
},
scrollableView:{
height: calcSize(40),
backgroundColor: '#000',
},
textCategory:{
fontSize: calcSize(25),
color:'#fff'
},
scrollableButton:{
flex:1,
margin:calcSize(30)
}
})
export default Styles
As you can see the black size is the scroll View,
I want it to be small.
In routes stack into dashboard screen, the style:
const Style = StyleSheet.create({
container: {
backgroundColor: '#9BC53D',
flex: 1,
justifyContent: 'space-around',
alignItems: 'center'
},
text: {
fontSize: 35,
color: 'white',
margin: 10,
backgroundColor: 'transparent'
},
button: {
width: 100,
height: 75,
margin: 20,
borderWidth: 2,
borderColor: "#ecebeb",
justifyContent: "center",
alignItems: "center",
borderRadius: 40
}
})
There is an existing limitation with ScrollView where height cannot be provided directly.
Wrap the ScrollView in another View and give height to that View.
Like,
render() {
return (
<View style={styles.container}>
<View style={{height: 80}} >
<ScrollView
horizontal
style={{ backgroundColor: 'blue'}}
>
<Text style={{padding: 24}} >title1</Text>
<Text style={{padding: 24}} >title2</Text>
<Text style={{padding: 24}} >title3</Text>
<Text style={{padding: 24}} >title4</Text>
<Text style={{padding: 24}} >title5</Text>
</ScrollView>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
});
snack sample: https://snack.expo.io/HkVDBhJoz
EXTRAS: Unlike height providing width to a ScrollView will work correctly
Use flexGrow:0 inside ScrollView style
<ScrollView style={{ flexGrow:0 }}>
That worked for me:
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={{ flexGrow: 1 }}>
...
</View>
</ScrollView>
Considering the issue that you are using fixed height for the header, and flex for the Routes maybe, the orientation for different devices would not scale well and would look weird.
Therefore you may consider switching it to the flex
Here is the example by adding flexGrow to the styles of the ScrollView since it accepts view props
<View style={{ flex: 1 }}>
<ScrollView style={{ flexGrow: 0.05, backgroundColor: 'red', paddingTop: 50 }} horizontal>
<View style={{ width: 100 }}><Text>Label</Text></View>
<View style={{ width: 100 }}><Text>Label</Text></View>
<View style={{ width: 100 }}><Text>Label</Text></View>
<View style={{ width: 100 }}><Text>Label</Text></View>
<View style={{ width: 100 }}><Text>Label</Text></View>
</ScrollView>
<View style={{ flex: 0.95, backgroundColor: 'green' }} />
</View>
and here's the link to the snack expo
Use maxHeight inside scrollview style
<ScrollView style={{ maxHeight: 40 }}>•••</ScrollView>
Setting minHeight: int, maxHeight: int to ScrollView should work where int is height in pixels.
example below
<ScrollView style={{ minHeight: 280, maxHeight: 260}}>
I want to create a fullsize-screen with the child-view (a video player) that is rendered over the full size of the screen. I only get it work when i pass explicitly width and height to the component.
But i know that there is a property called "flex". In many tutorials they do something like "flex: 1", but for me it nowhere does what it is supposed to.
(For the sake of completeness, the video-player is not part of the question. I can also replace the <video> tag with <Image> or each other kind of view and get the same results)
render() {
const uri = this.props.uri
return (
<KeyboardAwareScrollView keyboardShouldPersistTaps="always" >
<TouchableWithoutFeedback onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={s.fullsize}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</KeyboardAwareScrollView>
)
}
My styles:
This is only working when i pass the width and height:
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
//flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
closeBtn: {
position: 'absolute',
left: 20,
top: 25,
},
});
I only tried out this one, but then the screen will be empty because of the -Component has a width and height of 0 each.
const s = StyleSheet.create({
fullsize: {
backgroundColor: 'black',
flex: 1, // this is not working
left: 0,
right: 0,
top: 0,
bottom: 0
},
});
I believe doing flex: 1 will make it take up all the space provided by it's parent element. In your case, none of your parent elements have any styling so try this out:
render() {
const uri = this.props.uri
return (
<View style={{ flex: 1 }}>
<TouchableWithoutFeedback style={{ flex: 1 }} onPress={RouterActions.pop}>
<Video source={{uri: uri}}
ref={(ref) => {
this.player = ref
}}
style={{ flex: 1 }}
/>
</TouchableWithoutFeedback>
<TouchableOpacity onPress={RouterActions.pop} style={s.closeBtn}>
<Icon name="times-circle-o" size={20} color="white" />
</TouchableOpacity>
</View>
)
}