React Native number of components in one row - reactjs

I treat it like an exercise. There are 28 components. I want to have maximum 4 rows of components, where in each row there is maximum 7 components and minimum 1 depending on screen size (28 rows then). Could you help me how to accomplish this? I know I can use flex-wrap: wrap style but I want to have maximum 7 components in a row. Help me please with this as Im new to React Native and Flexbox.
<View style={styles.container}>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'red'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'green'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'yellow'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'brown'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'blue'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'red'}}></View>
<View style={{ flex: 1, margin: 2, height: 40, backgroundColor: 'green'}}></View>
.
.
.
</View>
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
},
});

This problem can't be solved in bootstrap way (column, row stuff etc). Flexbox lacks the capability to make specific number of components appear in a row depending upon screen size.
You need to use ListView such that ListView renders 7 components in a row. ListView's renderRow function iterates over an array and displays content of each index of that array in a row. So, at each index you need to define json containing 7 attributes and return all these attributes in the form of an array to make them appear in a row.
In my project, I had to show 3 or less images in a row (as displayed in Media section of attached image) so I used the above mentioned approach to achieve this. Please find below my code. This might help you.
constructor() {
// data saved in state
this.state = {
mediaFiles: [{image: "image link"},
{image: "image link"},
{image: "image link"},
{image: "image link"},
{image: "image link"},
{image: "image link"},
{image: "image link"},
{image: "image link"},
],
dataSource: ds.cloneWithRows([])
}
}
componentDidMount() {
var temp = [];
//to ensure three images appear in a row
for (var index = 0; index < this.state.mediaFiles.length; index = index + 3) {
temp.push({
image1: this.state.mediaFiles[index].image,
image2: (index + 1) < this.state.mediaFiles.length ? this.state.mediaFiles[index + 1].image : null,
image3: (index + 2) < this.state.mediaFiles.length ? this.state.mediaFiles[index + 2].image : null
});
}
this.setState({
dataSource: this.state.dataSource
.cloneWithRows(temp)
});
}
renderMediaThumbnails(rowData) {
var mediaThumbnails = [],
key = 0;
for (var attribute in rowData) {
if (rowData[attribute]) {
mediaThumbnails.push(
<View key={key++} style={Styles.imageWrapper}>
<Image style={Styles.media} source={{uri: rowData[attribute]}} />
</View>)
}
}
return mediaThumbnails
}
renderRow(rowData) {
return (
<View style={Styles.mediaWrapper}>
{this.renderMediaThumbnails(rowData)}
</View>
);
}
render() {
return (
<View style={Styles.statusAndMedia}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)} />
</View>
);
}
Following are the styles:
mediaWrapper: {
flex: 1,
flexDirection: "row",
justifyContent: "space-around",
marginVertical: 3,
},
imageWrapper: {
borderWidth: 2,
borderColor: "#AE8B8C"
},
media: {
height: 50,
width: 85,
resizeMode: "stretch"
}

Ok I think I found the solution. I set a width of a parent View to the with of screen or to the 294 (width of 7 elements + margins) if the screen size is larger. Maybe hardcoding it to 294 would be enough but Im not able to check it on small screen now.
var width = Dimensions.get('window').width;
class Schedule extends React.Component {
render() {
return (
<View style={styles.container}>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'red'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'green'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'yellow'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'brown'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'blue'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'red'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'green'}}></View>
<View style={{ margin: 1, width: 40, height: 40, backgroundColor: 'red'}}></View>
</View>
)
}
};
export default Schedule;
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
width: width > 294 ? 294 : width
},
});

ListView is deprecated now , you can use SectionList or FlatList instead.

Related

How can I achieve this 3 square layout in react native?

I am quite new with React native and I'm not sure how to implement this design:
Grid layout
I have 20px horizontal padding around the whole app and I want to size these squares so they would form like a large rectangle with these gaps in between. I don't really want to hardcode these sizes.
I managed to get the design without any gaps by dividing the total width by 3 and then giving the big square 2/3 and the small squares 1/3. But how can I do this with the gaps ?
const themedStyles = useThemedStyles();
const width = Dimensions.get('window').width - 40;
return (
<View style={styles.container}>
<View style={styles.textContainer}>
<ThemedText style={themedStyles.subHeader}>Trending</ThemedText>
<ThemedText style={[themedStyles.accentText, {fontWeight: 'bold'}]}>
See all
</ThemedText>
</View>
<View style={styles.cardContainer}>
<View
style={{
width: (width / 3) * 2,
height: (width / 3) * 2,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
<View>
<View
style={{
width: width / 3,
height: width / 3,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
<View
style={{
width: width / 3,
height: width / 3,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
</View>
</View>
</View>
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-between',
},
textContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
cardContainer: {
flexDirection: 'row',
},
});
See below Code maybe it will help you:
import React from 'react';
import { View, Image, StyleSheet,Dimensions } from 'react-native';
const width = Dimensions.get('window').width - 40;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'space-between',
padding:20
},
textContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
flex:1,
backGroundColor:'red'
},
cardContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
},
});
const DisplayAnImage = () => {
return (
<View style={styles.container}>
<View style={styles.cardContainer}>
<View
style={{
width: (width / 3) * 2,
height: (width / 3) * 2,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
<View style={{justifyContent: 'space-between'}}>
<View
style={{
width: width / 3.5,
height: width / 3.5,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
<View
style={{
width: width / 3.5,
height: width / 3.5,
backgroundColor: 'white',
borderWidth: 2,
}}></View>
</View>
</View>
</View>
);
}
export default DisplayAnImage;

Flex value for main view causing all of my content to dissapear

I am working on a react native coffee app and trying to create the lay out where I have
the header
the scrollable container holding my coffee
a footer with a text input
a '+' icon to add the coffees to the container
Currently the flex value of my main 'View' is causing my entire app to push to the top of the screen and i have no idea why. I can set the height and width to 100% without flex and i can fill the screen but then i cannot configure my footer properly (+ i know this is bad practice..)
What am i doing wrong with my styles?
const Main = () => {
return(
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.headerText}> List of Coffee </Text>
</View>
<ScrollView styles={styles.scrollContainer}>
</ScrollView>
<View styles={styles.footer}>
<TextInput
styles={styles.textInput}
placeholder="add your latest coffee here"
placeholderTextColor="#EEE">
</TextInput>
</View>
<TouchableOpacity style={styles.addButton}>
<Text style={styles.addButtonText}>+</Text>
</TouchableOpacity>
</View>
);};
const styles = StyleSheet.create({
container: {
flex:1,
flexDirection:"column",
backgroundColor: 'grey',
},
footer: {
position: 'absolute',
bottom: 10,
left: 10,
right: 10,
backgroundColor: 'white',
zIndex: 9,
},
header: {
backgroundColor: 'black',
alignItems: 'center',
justifyContent: 'center',
borderBottomWidth: 10,
borderBottomColor: 'yellow' ,
paddingTop: 20,
},
headerText: {
color: 'white',
fontSize: 36,
padding: 26,
fontWeight: "500",
fontFamily: 'Helvetica',
},
scrollContainer:{
marginBottom: 100,
},
textInput: {
alignSelf: 'stretch',
color: 'black',
padding:20,
backgroundColor: 'black',
borderTopWidth: 2,
borderTopColor: 'black',
fontSize: 32,
},
addButton: {
position: 'absolute',
zIndex: 11,
right: 20,
bottom: 100,
backgroundColor: 'black',
width: 80,
height: 80,
borderRadius: 50,
justifyContent: 'center',
alignItems: 'center',
} ,
addButtonText:{
color: '#FFD700',
fontSize: 36,
fontWeight: '700',
},
});
export default Main;

Custom React Native Slider

Does anyone know how to render a slider in react-native like the below, with distinct sections? So far, have only seen sliders with one line and one dot.
You can achieve this type of slider by using LayoutAnimation. Find the below code to get it done.
import React, { useState } from 'react';
import {Button,LayoutAnimation,Platform,StyleSheet,Text,UIManager,View} from 'react-native';
if (Platform.OS === 'android') {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
const step = ['1', '2', '3', '4', '5', '6', '7'];
const activeColor = '#444';
export default function TrackingStatus() {
const [activeIndex, setActive] = useState(0);
const setActiveIndex = (val) => {
LayoutAnimation.easeInEaseOut();
setActive(val);
};
const marginLeft = (100 / (step.length - 1)) * activeIndex - 100 + '%';
return (
<View style={styles.constainer}>
<Text style={styles.prop}>{activeIndex}</Text>
<View style={styles.statusContainer}>
<View style={styles.line}>
<View style={[styles.activeLine, { marginLeft }]} />
</View>
{step.map((step, index) => (
<View style={[styles.dot]} key={step}>
<View
style={[
index <= activeIndex
? { height: '100%', width: '100%' }
: { height: '0%', width: '0%' },
{ backgroundColor: activeColor, borderRadius: 20 },
]}
/>
</View>
))}
</View>
<View style={styles.btns}>
<Button
title="prev"
onPress={() => setActiveIndex(activeIndex - 1)}
disabled={activeIndex <= 0}
/>
<Button
title="next"
onPress={() => setActiveIndex(activeIndex + 1)}
disabled={activeIndex >= step.length - 1}
/>
</View>
</View>
);
}
const styles = StyleSheet.create({
constainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 30,
},
statusContainer: {
flexDirection: 'row',
alignItems: 'center',
width: '100%',
height: 70,
justifyContent: 'space-between',
},
dot: {
height: 15,
width: 15,
borderRadius: 10,
backgroundColor: '#ccc',
overflow: 'hidden',
alignItems: 'center',
justifyContent: 'center',
},
line: {
height: 5,
width: '100%',
backgroundColor: '#ccc',
position: 'absolute',
borderRadius: 5,
overflow: 'hidden',
},
activeLine: {
height: '100%',
width: '100%',
backgroundColor: activeColor,
borderRadius: 5,
},
btns: {
width: '100%',
flexDirection: 'row',
justifyContent: 'space-evenly',
marginTop: 20,
},
prop: {
marginBottom: 20,
width: 100,
textAlign: 'center',
},
});
In the above code, We simply create a UI clone as required and add margin-left as well as filling dots with colour. just before changing the state of margin-left & dot colour, we added layout animation to add an animation effect for progress.
Output
I hope this explanation will help you understand the working

2 column Flatlist component not distributing columns properly

I have a flatlist component that is 2 rows but the columns don't distribute properly. I'm having trouble styling it to achieve this.
My Flatlist:
<FlatList
contentContainerStyle={styles.list}
numColumns={2}
data={this.props.route.params.data}
keyExtractor={(item, index) => item.id}
renderItem={({item}) => (
<TouchableOpacity
style={styles.view}
onPress={() =>
item.subcategories
? this.props.navigation.navigate('Browse', {
screen: 'Subcategories',
params: {data: item.subcategories},
})
: this.props.navigation.navigate('Browse', {
screen: 'Products',
params: {id: item.id},
})
}
underlayColor="grey">
<View style={styles.view}>
<Text style={styles.title}>{item.title}</Text>
</View>
</TouchableOpacity>
)}
/>
Style:
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
list: {
flexDirection: 'column',
},
view: {
backgroundColor: 'grey',
alignItems: 'center',
justifyContent: 'center',
alignContent: 'stretch',
margin: 1,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 2,
},
title: {
textAlign: 'center',
fontSize: 20,
padding: 5,
width: '100%',
},
});
What it looks like:
I know I can set the width to a number, but I want it to dynamically change with screen size.
Can you try add width: '50%' to the view style or width: Dimensions.get('window').width / 2

ReactNative Flat list Odd cards full width

i have 12 card view on homescreen
im using Flat list to render the 12 cards
I have 2 columns set so 2 cards in the view
i would like to tweak it so after every 2 cards 1 card be full width then the next 2 cards next to each other and the third is full width and so on
here is my code
<FlatList
data={ this.state.GridListItems }
keyExtractor={(item) => item.id.toString()}
renderItem={ ({item, index}) =>
<TouchableOpacity style={{width: (index + 1) % 3 === 0 ? '108%' : '54%', height: '110%', justifyContent:'center', overflow: "hidden",
flex:1,
justifyContent: 'center',
alignItems: 'center',
margin: 5,
backgroundColor: '#231F20',
borderRadius: 5,
shadowColor: '#000',
shadowOffset: { width: 5, height: 5 },
shadowOpacity: 0.8,
shadowRadius: 5,
elevation: 6,
padding: 5,}}
onPress={() => this.props.navigation.navigate('ProfileScreen', { height: "6'2", category: item.key })}>
<ImageBackground
source={{ uri: item.img }}
style={{width: '108%', height: '110%', justifyContent:'center'}} >
<View style={{position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center'}}>
<Text style={styles.GridViewTextLayout} > {item.key} </Text>
</View>
</ImageBackground>
</TouchableOpacity>
}
numColumns={2}
initialNumToRender={6}
/>
i want it to render 2 cards (each card 50% of screen) and every third card (100% of the screen) for the 12 element
you can try this. so, on every third card, it should display the full width.
const styles = {
GridViewContainer: {
justifyContent:'center',
overflow: "hidden",
flex:1,
justifyContent: 'center',
alignItems: 'center',
margin: 5,
backgroundColor: '#231F20',
borderRadius: 5,
shadowColor: '#000',
shadowOffset: { width: 5, height: 5 },
shadowOpacity: 0.8,
shadowRadius: 5,
elevation: 6,
padding: 5,
}
}
<FlatList
data={this.state.GridListItems}
keyExtractor={(item) => item.id.toString()}
renderItem={({item, index}) =>
<TouchableOpacity
style={styles.GridViewContainer}
onPress={() => this.props.navigation.navigate('ProfileScreen', { height: "6'2", category: item.key })}
>
<ImageBackground
source={{ uri: item.img }}
style={{
width: (index + 1) % 3 === 0 ? '216%' : '108%',
height: '110%',
justifyContent: 'center',
}}
>
<View
style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'center',
alignItems: 'center'
}}
>
<Text style={styles.GridViewTextLayout}> {item.key} </Text>
</View>
</ImageBackground>
</TouchableOpacity>
}
numColumns={2}
initialNumToRender={6}
/>
edited to reflect the styling for GridViewContainer

Resources