I am developing a React Native application for Learning purposes. Now I am using FlatList in my application. But I am having a problem. My flat list is not scrolling to see all the items. It bounces back when I tried to scroll it out of screen size. I used the solutions I found online adding the flex to 1 on the root, but it is not working.
Here is my code:
class Events extends React.Component {
static navigationOptions = {
title: "Events"
};
constructor(props) {
super(props);
this.state = {
data: [
{
id: 1,
name: "Name 1"
},
{
id: 2,
name: "Name 2"
},
{
id: 3,
name: "Name 3"
},
{
id: 4,
name: "Name 4"
},
{
id: 5,
name: "Name 5"
},
{
id: 6,
name: "Name 6"
},
{
id: 7,
name: "Name 7"
},
{
id: 8,
name: "Name 8"
},
{
id: 9,
name: "Name 9"
},
{
id: 10,
name: "Name 10"
},
{
id: 11,
name: "Name 11"
},
{
id: 12,
name: "Name 12"
},
{
id: 13,
name: "Name 13"
},
{
id: 14,
name: "Name 14"
},
{
id: 15,
name: "Name 15"
},
{
id: 16,
name: "Name 16"
},
{
id: 17,
name: "Name 17"
},
{
id: 18,
name: "Name 18"
},
{
id: 19,
name: "Name 19"
},
{
id: 20,
name: "Name 20"
},
{
id: 21,
name: "Name 21"
},
{
id: 22,
name: "Name 22"
},
{
id: 23,
name: "Name 23"
},
{
id: 24,
name: "Name 24"
}
]
};
}
_handleLoadMore() {}
renderItem(item) {
return (
<Card>
<CardItem>
<Left>
<Thumbnail source={{ uri: "https://www.vkguy.co.uk/images/slideshow/05.jpg" }} />
<Body>
<Text>NativeBase</Text>
<Text note>GeekyAnts</Text>
</Body>
</Left>
</CardItem>
<CardItem cardBody>
<Image
source={{ uri: "https://www.vkguy.co.uk/images/slideshow/05.jpg" }}
style={{ height: 200, width: null, flex: 1 }}
/>
</CardItem>
<CardItem>
<Left>
<Button transparent>
<Icon active name="thumbs-up" />
<Text>12 Likes</Text>
</Button>
</Left>
<Body>
<Button transparent>
<Icon active name="chatbubbles" />
<Text>4 Comments</Text>
</Button>
</Body>
<Right>
<Text>11h ago</Text>
</Right>
</CardItem>
</Card>
);
}
render() {
return (
<FlatList
contentContainerStyle={{
flex: 1,
flexDirection: "column",
height: "100%",
width: "100%"
}}
data={this.state.data}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => this.renderItem(item)}
onEndReached={this._handleLoadMore}
/>
);
}
}
export default Events;
But it is not scrolling to see the items off the screen. How can I fix it?
Here is the screenshot. I cannot scroll down more than that.
Change some styling in FlatList view and add View as main container
render() {
return (
<View style={{ flex: 1, width: '100%' }}>
<FlatList
data={this.state.data}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => this.renderItem(item)}
onEndReached={this._handleLoadMore}
/>
</View>
);
}
Try with
<FlatList
contentContainerStyle={{ flexGrow: 1, paddingBottom: 5 }}
data={this.state.data}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => this.renderItem(item)}
onEndReached={this._handleLoadMore}
onEndReachedThreshold={0.5}
/>
Related
what happens is the following fact: I want to add "switch/toggle" next to id 2.3 and 4 items, but it's getting on all items on the flatlist. I used "import SwitchSelector from "react-native-switch-selector"; " Can anyone who understands flatlist help me? I will be very happy and grateful
I'm not getting it at all, and I'm thinking of redoing my code because of this problem
Settings.js
import { SafeAreaView,Text, View, FlatList,
TouchableOpacity, StyleSheet, Image} from 'react-native';
import SwitchSelector from "react-native-switch-selector";
import React, {useState} from 'react'
const Settings = () => {
const [data, setdata] = useState(DATA);
const [isRender, setisRender] = useState(false);
const DATA = [
{id: 1, text: 'Perfil', image: require('../../assets/images/user.png')},
{id: 2, text: 'Dark/Light mode', image: require('../../assets/images/light-up.png')},
{id: 3, text: 'TouchId', image: require('../../assets/images/fingerprint.png')},
{id: 4, text: 'Notificações', image: require('../../assets/images/bell-fill.png')},
//{id: 5, text: 'Logout'},
]
const renderItem = ({item}) => {
return(
<TouchableOpacity style= {styles.item}>
<View style={ styles.avatarContainer }>
<Image source={ item.image } style={ styles.avatar } />
</View>
<View>
<Text style={styles.text}>{item.text}</Text>
</View>
</TouchableOpacity>
)
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
extraData={isRender}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20
//marginHorizontal: 21
},
item:{
borderBottomWidth: 1,
borderBottomColor: '#808080',
alignItems: 'flex-start',
flexDirection: 'row',
},
avatarContainer: {
backgroundColor: 'transparent',
//borderRadius: 100,
height: 30,
width: 30,
justifyContent: 'center',
alignItems: 'center'
},
avatar: {
height: 25,
width: 25,
bottom: -25,
marginLeft: 30
},
text:{
marginVertical: 30,
fontSize: 20,
fontWeight: 'bold',
marginLeft: 30,
marginBottom: 10,
bottom: 5
}
});
export default Settings;
Well here is my approach to your problem as much as I understood;
it also actively changes the values of data for further usage of App
const [data, setData] = useState([
{ id: 1, text: 'Perfil', image: require('../../assets/images/user.png'), chosen: false },
{ id: 2, text: 'Dark/Light mode', image: require('../../assets/images/light-up.png'), chosen: false },
{ id: 3, text: 'TouchId', image: require('../../assets/images/fingerprint.png'), chosen: false },
{ id: 4, text: 'Notificações', image: require('../../assets/images/bell-fill.png'), chosen: false },
//{id: 5, text: 'Logout'},
])
const [isRender, setisRender] = useState(false);
const renderItem = ({ item }) => {
return (
<TouchableOpacity style={styles.item}>
<View style={styles.avatarContainer}>
<Image source={item.image} style={styles.avatar} />
</View>
<View>
<Text style={styles.text}>{item.text}</Text>
</View>
{item.id > 1 && <Switch
thumbColor={item.chosen == false ? "red" : "green"}
value={item.chosen}
onChange={() => setData(data.map(index =>
item.id === index.id
? {...index, chosen : !index.chosen}
: index
))}
/>}
</TouchableOpacity>
)
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
extraData={isRender}
/>
</SafeAreaView>
);};
You could try adding a boolean property to each item in DATA. You could then conditionally show a <SwitchSelector /> in renderItem based on that property.
For example, below I added the property showSwitch and used a ternary expression to conditionally show a <SwitchSelector /> in renderItem:
const DATA = [
{id: 1, text: 'Perfil', image: require('../../assets/images/user.png'), showSwitch: false},
{id: 2, text: 'Dark/Light mode', image: require('../../assets/images/light-up.png'), showSwitch: true},
{id: 3, text: 'TouchId', image: require('../../assets/images/fingerprint.png'), showSwitch: true},
{id: 4, text: 'Notificações', image: require('../../assets/images/bell-fill.png'), showSwitch: true},
//{id: 5, text: 'Logout'}
]
...
const renderItem = ({ item }) => {
return (
<TouchableOpacity style={styles.item}>
<View style={styles.avatarContainer}>
<Image source={item.image} style={styles.avatar} />
</View>
<View>
<Text style={styles.text}>{item.text}</Text>
</View>
{item.showSwitch ? <SwitchSelector /> : null}
</TouchableOpacity>
);
};
This works from me:
import { SafeAreaView,Text, View, FlatList,
TouchableOpacity, StyleSheet, Image, Switch} from 'react-native';
import React, {useState, useEffect} from 'react';
import {TouchID} from 'react-native-touch-id';
const Settings = () => {
const [data, setdata] = useState(DATA);
const [isRender, setisRender] = useState(false);
const DATA = [
{id: 1, text: 'Perfil', image: require('../../assets/images/user.png'), switch: false},
{id: 2, text: 'Dark/Light mode', image: require('../../assets/images/light-up.png'), switch: true},
{id: 3, text: 'TouchId', image: require('../../assets/images/fingerprint.png'), switch: true},
{id: 4, text: 'Notificações', image: require('../../assets/images/bell-fill.png'), switch: true},
//{id: 5, text: 'Logout'}
]
const renderItem = ({item}) => {
return(
<TouchableOpacity
style= {styles.item}
>
<View style={ styles.avatarContainer }>
<Image source={ item.image } style={ styles.avatar } />
</View>
<View>
<Text style={styles.text}>{item.text}</Text>
</View>
{item.switch ? <Switch /> : null}
</TouchableOpacity>
)
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
keyExtractor={(item) => item.id.toString()}
renderItem={renderItem}
extraData={isRender}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20
//marginHorizontal: 21
},
item:{
borderBottomWidth: 1,
borderBottomColor: '#808080',
alignItems: 'flex-start',
flexDirection: 'row',
},
avatarContainer: {
backgroundColor: 'transparent',
//borderRadius: 100,
height: 30,
width: 30,
justifyContent: 'center',
alignItems: 'center',
},
avatar: {
height: 25,
width: 25,
bottom: -25,
marginLeft: 30
},
text:{
marginVertical: 30,
fontSize: 20,
fontWeight: 'bold',
marginLeft: 30,
marginBottom: 10,
bottom: 5
},
});
export default Settings;
I would like to display selected items, but the situation I have got is not showing images but only the backgroundColor. There is image uri in productData, so I would like to display the uri. Another issue I am having is that the CheckBox is not checked.
My code below :
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 100,
},
topContainer: {
flexDirection: 'row',
marginHorizontal: 15,
paddingBottom: 20,
paddingTop: 10,
borderBottomWidth: 0.5,
borderColor: colors.very_light_pink,
},
iconWrap: {
width: 60,
height: 60,
borderRadius: 6,
backgroundColor: '#cccccc',
alignItems: 'center',
justifyContent: 'center',
marginRight: 5,
},
icon: {
width: 20,
height: 20,
},
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginHorizontal: 15,
marginBottom: 15,
},
});
const productData = [
{
id: 0,
productName: 'a',
price: 45000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/8211/407/250/73/w/563/8211407250_2_1_1.jpg?ts=1613387641299',
},
{
id: 1,
productName: 'b',
price: 45000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/0526/407/707/2/w/1280/0526407707_6_1_1.jpg?ts=1610451943259',
},
{
id: 2,
productName: 'c',
price: 129000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/0706/442/710/2/w/563/0706442710_2_1_1.jpg?ts=1614254205907',
},
{
id: 3,
productName: 'd',
price: 139000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/0706/431/446/2/w/563/0706431446_2_1_1.jpg?ts=1613386951068',
},
{
id: 4,
productName: 'e',
price: 189000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/4162/536/401/2/w/1280/4162536401_1_1_1.jpg?ts=1613122054256',
},
{
id: 5,
productName: 'f',
price: 89000,
image:
'https://static.zara.net/photos///2021/V/0/2/p/4165/537/430/2/w/563/4165537430_1_1_1.jpg?ts=1613122064808',
},
];
const LiveAddProducts = (props) => {
const [productClicked, setProductClicked] = useState(null);
const [selectedProducts, setSelectedProducts] = useState([]);
useEffect(() => {
if (productClicked !== null) {
setSelectedProducts((prevState) => [
...prevState,
// I believe this should be changed
{ id: prevState.length, image: productClicked },
]);
setProductClicked(null);
}
}, [productClicked]);
console.log(selectedProducts.image);
return (
<View style={[styles.container]}>
<View style={[styles.topContainer]}>
<TouchableOpacity style={[styles.iconWrap]}>
<Image source={assets.live_add_icon} style={[styles.icon]} />
</TouchableOpacity>
{selectedProducts.map((item, index) => (
<TouchableOpacity style={[styles.iconWrap]} key={item.id}>
<Image source={{ uri: productData.image }} style={[styles.icon]} />
</TouchableOpacity>
))}
</View>
<ScrollView style={{ paddingTop: 20 }}>
{productData.map((item, index) => (
<View style={[styles.row]}>
<ProductsList
price={item.price}
image={item.image}
productName={item.productName}
/>
// Any problems here?
<CheckBox
size={20}
onPress={() => setProductClicked(index)}
source={
productClicked === index
? assets.icon_check_on
: assets.checked_off
}
/>
</View>
))}
</ScrollView>
<BottomButton
btnText={'Add Items'}
buttonStyle={{ height: 50, backgroundColor: colors.iris }}
/>
</View>
);
};
LiveAddProducts.propTypes = {};
export default LiveAddProducts;
The image which has problems :
useEffect(() => {
if (productClicked !== null) {
setSelectedProducts((prevState) => [
...prevState,
// I believe this should be changed
{ id: prevState.length, image: productClicked },
]);
setProductClicked(null);
}
}, [productClicked]);
Here, you'd want to replace this with the index of the element
id: prevState.length
Also,
{selectedProducts.map((item, index) => (
<TouchableOpacity style={[styles.iconWrap]} key={item.id}>
<Image source={{ uri: productData.image }} style={[styles.icon]} />
</TouchableOpacity>
))}
Here, the uri should be item.image, as that is where you hold the data for your item when it is rendered in this loop
I am tasked right now with making a screen that gives options in a grid of two columns with multiple cards as the items of a list. (You can see here)
I was trying to create a row flexbox, but it ended up simply continuing horizontally forever.
I'd like to know what would be a good way to get this effect os two columns expanding downwards.
You should use FlatList and set numColumns prop to "2" to show FlatList as grid
Here is complete code sample
import React from "react";
import { SafeAreaView, FlatList, Text, View, Image } from "react-native";
const DATA = [
{
id: "1",
title: "RUSTY DRIVE",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "2",
title: "SABOR MORENO",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "3",
title: "0 MESTRE PUB",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "4",
title: "GRILL 54 CHEF",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "5",
title: "RUSTY DRIVE",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "6",
title: "SABOR MORENO",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "7",
title: "0 MESTRE PUB",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
},
{
id: "8",
title: "GRILL 54 CHEF",
image:
"https://res.cloudinary.com/demo/image/upload/w_260,h_200,c_crop,g_north/sample.jpg"
}
];
export default class App extends React.Component {
_renderItem = ({ item }) => (
<View style={{ flex: 1, marginHorizontal: 20, marginBottom: 20 }}>
<Image
style={{ width: "100%", height: 140 }}
source={{ uri: item.image }}
/>
<Text style={{ textAlign: "center", marginTop: 8 }}>{item.title}</Text>
</View>
);
render() {
return (
<SafeAreaView style={{ flex: 1 }}>
<FlatList
data={DATA}
renderItem={this._renderItem}
keyExtractor={item => item.id}
numColumns={2}
style={{ flex: 1 }}
contentContainerStyle={{ paddingVertical: 20 }}
/>
</SafeAreaView>
);
}
}
App Preview
You can achieve this by simply doing something like:
<View style={{flex:1, flexDirection:"row", maxHeight:100}}>
<View style={{flex : 1, backgroundColor:"red"}}/>
<View style={{flex : 1, backgroundColor:"yellow"}}/>
</View>
Then adding new views will let you achieve such a layout, then it's up to you deciding what and how many you want to render
I want to show a flatlist but I have a problem. In the bottom of the phone, I can't show the integrality of the post and don't scroll to it. I don't know why. Some issues ? I have try some spacing stuff with style but that's don't work like I want.
import React, { Component } from 'react'
import { View, Text, FlatList } from 'react-native'
import BlockPhoto from '../ui/BlockPhoto';
import { isTemplateElement } from '#babel/types';
interface Props { }
interface State { monTabPost: Array<TabPost> }
interface TabPost { id: number, title: string, }
const monTabPost: Array<TabPost> = [
{ id: 1, title: "Chat 1", },
{ id: 2, title: "Chat 2", },
{ id: 3, title: "Chat 3", },
{ id: 4, title: "Chat 4", },
{ id: 5, title: "Chat 5", },
{ id: 6, title: "Chat 6", },
{ id: 7, title: "Chat 7", },
{ id: 8, title: "Chat 8", },
]
export default class VueFlatList extends Component<Props, State> {
state = {
monTabPost: monTabPost ? monTabPost : []
}
render = () => {
return (
<View style={{ paddingHorizontal: 30 }}>
<Text style={{ paddingVertical: 20, backgroundColor: "black", marginBottom: 5, color: "white", textTransform: "uppercase", textAlign: "center", fontWeight: "bold" }}>Mon titre</Text>
<FlatList
//inverted
data={this.state.monTabPost}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) =>
<View>
<Text>Mon post</Text>
<BlockPhoto title={item.title} />
</View>
}
/>
</View>
)
}
}
Add contentContainerStyle={{ paddingBottom: 100 // <-- you can calibrate this }} on Flatlist component.
can replace your render to below it will work,
render = () => {
return (
<View style={{ paddingHorizontal: 30, flex: 1, width: '100%' }}>
<Text style={{ paddingVertical: 20, backgroundColor: "black", marginBottom: 5, color: "white", textTransform: "uppercase", textAlign: "center", fontWeight: "bold" }}>Mon titre</Text>
<FlatList
data={this.state.monTabPost}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) =>
<View>
<Text>Mon post</Text>
<BlockPhoto title={item.title} />
</View>
}
/>
</View>
)
}
you have to add some styling in main container view, flex occupy the entire lists of data.
I tried using paddingBottom, but it didnt work for me.
Try adding marginBottom: 150 on the Flatlist component's style.
I am using Picker and DeckSwiper from NativeBase, when I select a different topic from the Picker menu, it updates the state that is read by dataSource on the DeckSwiper component, which should re render to show the new content. It currently only re renders if the first card is swiped. How can I get it re render as soon as the state has changed? Here is a GIF to show how it's currently working.
Here is the code
const Item = Picker.Item;
const topics = [
{ label: "topic 1", value: "1" },
{ label: "topic 2", value: "2" },
{ label: "topic 3", value: "3" }
];
const cards = [
{ text: "Card A", topicId: "1", name: "One" },
{ text: "Card B", topicId: "2", name: "Two" },
{ text: "Card C", topicId: "3", name: "Three" },
{ text: "Card D", topicId: "1", name: "Four" },
{ text: "Card E", topicId: "2", name: "Five" },
{ text: "Card F", topicId: "3", name: "Six" }
];
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selected: "1",
topics: topics,
cards: cards
};
}
onValueChange(value: string) {
this.setState({
selected: value,
cards: cards.filter(item => item.topicId === value)
});
}
render() {
return (
<Container>
<Header />
<Content>
<Form>
<Picker
iosHeader="Select one"
mode="dropdown"
selectedValue={this.state.selected}
onValueChange={this.onValueChange.bind(this)}
>
{this.state.topics.map((topic, i) => {
return <Item label={topic.label} value={topic.value} key={i} />;
})}
</Picker>
</Form>
<View>
<DeckSwiper
ref={c => (this._deckSwiper = c)}
dataSource={this.state.cards}
renderItem={item => (
<Card style={{ elevation: 3 }}>
<CardItem>
<Left>
<Body>
<Text>{item.text}</Text>
<Text>Topic{item.topicId}</Text>
</Body>
</Left>
</CardItem>
<CardItem cardBody>
<Image
style={{ height: 300, flex: 1 }}
source={{
uri:
"http://www.pixedelic.com/themes/geode/demo/wp-content/uploads/sites/4/2014/04/placeholder4.png"
}}
/>
</CardItem>
</Card>
)}
/>
</View>
</Content>
<View style={{ flexDirection: "row", flex: 1, position: "absolute", bottom: 50, left: 0, right: 0, justifyContent: 'space-between', padding: 15 }}>
<Button iconLeft onPress={() => this._deckSwiper._root.swipeLeft()}>
<Text>Swipe Left</Text>
</Button>
<Button iconRight onPress={() => this._deckSwiper._root.swipeRight()}>
<Text>Swipe Right</Text>
</Button>
</View>
</Container>
);
}
}