react-native: flatlist with dynamic numColumns depending json params - react-native

This is my json:
{
data:[
{id:1,type:0...},{id:2,type:0...},{id:3,type:1...},
]
}
and Flatlist
_keyExtractor = (item,index) => item.id.toString();
<FlatList
data={this.state.products}
renderItem={this.renderItem}
keyExtractor={this._keyExtractor}
numColumns={3}
/>
I want something like this:
numColumns={item.type ? 1 : 2}
Is it possible?

Unfortunately, numColumns sets the number of columns for the global FlatList, but it is possible to create a similar behavior in your renderItem function.
Of course it is possible that you have to modify the structure of your data beforehand, so that this workaround is applicable for you.
See example below:
Data:
this.state={
data:[
{id:1,type:0},{id:2,type:1},{id:3,type:1},
{id:4,type:0},{id:5,type:1},{id:6,type:0},
]
}
Render:
const WIDTH = Dimensions.get('window').width; // get the screen width
renderItem({item}){
// if type == 0, render two views side by side
if (item.type == 0){
return(
<View style={{width: WIDTH, flexDirection: 'row'}}>
<View style={{ backgroundColor: 'red', width: WIDTH/2 }}>
<Text> {item.id} a) </Text>
</View>
<View style={{ backgroundColor: 'blue', width: WIDTH/2 }}>
<Text> {item.id} b) </Text>
</View>
</View>
);
}else{
// otherwise render only one item
return (
<View style={{ backgroundColor: 'green', width: WIDTH }}>
<Text> {item.id} </Text>
</View>
);
}
}
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.data}
keyExtractor={(item) => item.id.toString()}
renderItem={(item) => this.renderItem(item)}
/>
</SafeAreaView>
);
}
Image:

Related

How to add text title with react native FlatGride?

Using the below code I can return FlatGrid according to my item list.
return (
<FlatGrid
itemDimension={130}
data={items}
style={styles.gridView}
// staticDimension={300}
// fixed
spacing={10}
renderItem={({ item }) => (
<ImageBackground source={ item.source} style={[styles.itemContainer, ]}>
<Text style={styles.itemName}>{item.name}</Text>
</ImageBackground>
)}
/>
);
But I want to add some text before the FlatGrid. So I update my code like below.
return (
<View>
<Text style={{color:'#000000'}}>This is the text</Text>
<FlatGrid
itemDimension={130}
data={items}
style={styles.gridView}
spacing={10}
renderItem={({ item }) => (
<ImageBackground source={ item.source} style={[styles.itemContainer, ]}>
<Text style={styles.itemName}>{item.name}</Text>
</ImageBackground>
)}
/>
</View>
);
But as the above code output, I can see only the text. ("This is the text").
I am new to react-native. Can anyone say how I fix this issue?
Try for SectionGrid
It will give you the title & list as well is same component
<SectionGrid
itemDimension={130}
sections={[
{
title: 'Numbers',
data: [1,2,3,4,5,6],
},
{
title: 'Alphabets',
data: ['A', 'B', 'C', 'D', 'E'],
},
]}
renderItem={({ item }) => (<Text>{item}</Text>)}
renderSectionHeader={({ section }) => (
<Text style={{ fontSize: 20 }}>{section.title}</Text>
)}
/>
This worked for me. Used different screen parts for each component.
return (
<View style={{flexDirection: "column",flex: 1, }}>
<View style={{ flex: 1,backgroundColor:'red' }}>
<Text style={{textAlign:'center',fontSize:20}}>This istext</Text>
</View>
<View style={{ flex: 10,backgroundColor:'blue'}}>
<FlatGrid
itemDimension={130}
data={items}
style={styles.gridView}
spacing={10}
renderItem={({ item }) => (
<ImageBackground source={ item.source} style={[styles.itemContainer, ]}>
<Text style={styles.itemName}>{item.name}</Text>
</ImageBackground>
)}/>
</View>
</View>
);

FlatList can't scroll horizontally

I'm trying to scroll horizontal but Its never move to the second or third item.
When I'm dragging left I cant see part of the second elements.
export const Flat: React.FC = () => {
const aaa = [{}, {}, {}]
const renderItem = ({item, index}: any) => {
if(index === 0){
return <View style={{width:"100%", backgroundColor: "green"}}>
<Text> asdasad </Text>
</View>
}
else if(index === 1){
return <View style={{width: "100%", backgroundColor: "red"}}>
<Text> asdasad </Text>
</View>
}
return <View style={{width: "100%", backgroundColor: "blue"}}>
<Text> asdasad </Text>
</View>
}
return <View style={{flex: 1}}>
<FlatList style={{flex: 1}}
contentContainerStyle={{ flex: 1}}
horizontal={true}
data={aaa}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
pagingEnabled={true}
// bounces={false}
/>
</View>
}
Here is - https://snack.expo.dev/RNeBkS7Rk , I just added <ScrollView horizontal> and change yours styles : width:"100%" to width:300

Horizontal Flatlist Re-Render changes ListHeaderComponent

I'm banging my head on this one. I am using a horizontal flat list. And putting a view in ListHeaderComponent to act as padding. You can see it as a blue box in the first image below. That works great. Trouble is, when I refresh the flatlist from elsewhere in my app, the padding seems to break. See the blue box in image 2 below. Code below images. Any help appreciated.
OnLoad:
After props refresh:
<FlatList
ref={(ref) => { this.flatListRef = ref; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
data={this.props.store.allBookData}
ListHeaderComponent={<View style={{ width: itemOffset / 2, height: 200, backgroundColor: 'blue' }}></View>}
ListFooterComponent={<View style={{ width: itemOffset /2 }}></View>}
renderItem={({ item }) =>(
<TouchableWithoutFeedback onPress={() => this.gotoBook(item)}>
<Animatable.View
animation={"fadeIn"}
easing={"ease-in-out"}
duration={320}
delay={240}
useNativeDriver={true}
style={[styles.bookItem, { width: itemWidth, height: itemHeight, marginTop: itemMarginTop, opacity: 1, marginRight: 24 }]}>
{/* cover */}
<View style={[styles.bookCover, { width: itemWidth, height: bookHeight, backgroundColor: global.setBookItemColor(item.color)}]}>
<View style={styles.bookBindingHolder}>
<View style={styles.bookBinding}></View>
</View>
<View style={styles.bookCoverPhotoHolder}>
<View style={styles.bookCoverPhoto}>
<FastImage style={styles.bookCoverFrame} resizeMode={'contain'} source={Images.coverFrameWithTape}/>
{(() => {
//display cover
if (item.cover == 'temp') {
//temp
return (<FastImage style={styles.bookCoverImg} resizeMode={'cover'} source={Images.tempCover} />)
}else{
//photo
return (
<FastImage
style={[styles.bookCoverImg, {opacity: 0.88}]}
resizeMode={'cover'}
source={{uri: item.cover}}>
</FastImage>
)
}
})()}
</View>
</View>
</View>
{/* title */}
<View style={styles.bookTitleHolder}>
<Text style={[global.TextStyles.h3, global.TextStyles.darkText, global.TextStyles.alignCenter]}>{item.title}</Text>
</View>
{/* count */}
<View style={styles.bookPhotoCountHolder}>
<View style={styles.bookPhotoCount}>
{(() => {
if (item.photoCount == global.maxPhotos){
var itemCount = 'Full'
}else{
var itemCount = item.photoCount + '/' + global.maxPhotos
}
return(
<Text style={[global.TextStyles.lightText, global.TextStyles.smallerLabel]}>{itemCount}</Text>
)
})()}
</View>
</View>
</Animatable.View>
</TouchableWithoutFeedback>
)}/>
It's hard to say where the problem comes from even though there's a good chance it comes from your itemOffset that you apply as width to your HeaderComponent.
However, I have the impression that you can make it easier to define paddings, instead of defining a header and footer component, you could simply apply paddings to the content of your list by passing to your list the props: contentContainerStyle
like that
<FlastList
...
contentContainerStyle={{
paddingHorizontal: itemOffset / 2
}}
/>
but I think you should look in your calcul of itemOffset

Last odd flatlist item stretches along screen

I'm displaying some data items with Flatlist in two columns. But when there is an odd item at the last place, it stretches all along the screen. I don't want that, how can I make it to take only %50 of screen space like even items? Thanks a lot.
CategoryGridTile Component:
return (
<View style={styles.PlaceItem}>
<TouchableCmp onPress={props.onSelect}>
<View>
<View style={{ ...styles.placeRow, ...styles.placeHeader }}>
<ImageBackground
source={{ uri: props.image }}
style={styles.bgImage}
>
<View style={styles.titleContainer}>
<Text style={styles.title} numberOfLines={2}>
{props.title}
</Text>
</View>
</ImageBackground>
</View>
</View>
</TouchableCmp>
</View>
)
}
const styles = StyleSheet.create({
PlaceItem: {
flex: 1,
height: 125,
width: '80%',
backgroundColor: '#f5f5f5',
borderRadius: 5,
overflow: 'hidden',
margin: 6
},
placeRow: {
flexDirection: 'row'
}
})
Screen:
const renderGridItem = itemData => {
return (
<CategoryGridTile
image={itemData.item.image}
title={itemData.item.title}
onSelect={() => {
props.navigation.navigate({
routeName: 'CategoryPlaces',
params: {
categoryId: itemData.item.title,
locationId: locations
}
})
}}
/>
)
}
return (
<FlatList
keyExtractor={(item, index) => item._id}
data={displayedCategories}
renderItem={renderGridItem}
numColumns={2}
/>
)
Changing PlaceItem flex: 1 to flex: 1/2 at CategoryGridTile component solved the problem.

Open a video player in react native

I'm working on an app where I need to display a player with an embed youtube video. I have retrieve the data of a youtube playlist, and put them in a TouchableHighlight. I want now to display a player in a WebView, but just on top of my list. So when I'm clicking on a video, the list go down and a WebView take the place ? Is it possible ? Here is the code :
Fetching data from YTApi
_fetchPlaylist(){
if(this.state.selectedIndex === 0) {
return(
fetch(`https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=${results}&playlistId=PLSlVQ0kIy6qx-f5O3J3GwIEOO5Y52z43-&key=${apiKey}`)
.then(res => res.json())
.then(res => {
const videoId = []
res.items.forEach(item => {
videoId.push(item)
})
this.setState({
data:videoId
})
})
.catch(error => {
console.error(error)
})
)
}
}
displayWebView
displayVideo(videoId){
if(this.state.selectedIndex == 0){
return(
<View style = {{height:300}}>
<WebView
style={{flex:1}}
javaScriptEnabled={true}
source={{uri: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`}}
/>
</View>
)
} else{
return <View></View>
}
}
render
<View style = {styles.containerPlaylist}>
<ScrollView>
<View style = {styles.body}>
{
this.state.data.map((item,i) =>
<TouchableHighlight
key = {item.id.videoId}
onPress = {()=> this.displayVideo(item.id.videoID)}>
<View style = {styles.vids}>
<Image
source = {{uri: item.snippet.thumbnails.medium.url}}
style = {{width: 120, height: 90,marginVertical:30, flexDirection:'column', alignSelf:'flex-start', backgroundColor:'#fff', resizeMode:'contain'}}
/>
<Text style = {styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
Thanks
You need to keep webview in your layout and then need to manage two state,
to show and hide webview/video
to change the url of webview/video
Initialize the state like this along with your other state:
this.state = {
showVideo: false,
videoUrl: "default youtube url or empty string"
}
then create your layout like this and adjust your styling as per your need:
<View style={styles.containerPlaylist}>
<ScrollView>
{this.state.showVideo &&
<View style={{ height: 300 }}>
<WebView
style={{ flex: 1 }}
javaScriptEnabled={true}
source={{ uri: this.state.videoUrl }}
/>
</View>
}
<View style={styles.body}>
{
this.state.data.map((item, i) =>
<TouchableHighlight
key={item.id.videoId}
onPress={() => this.displayVideo(item.id.videoID)}>
<View style={styles.vids}>
<Image
source={{ uri: item.snippet.thumbnails.medium.url }}
style={{ width: 120, height: 90, marginVertical: 30, flexDirection: 'column', alignSelf: 'flex-start', backgroundColor: '#fff', resizeMode: 'contain' }}
/>
<Text style={styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
and change your function something like this to display video which has been clicked:
displayVideo(videoId) {
this.setState({
videoUrl: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`
showVideo: true
});
}