React Native - trigger scrolling of FlatList outside the FlatList - react-native

I have a vertical FlatList component and two buttons as TouchableOpacity, how do I perform scrolling of the FlatList with the buttons,
i.e. 'scrolling the FlatList towards bottom` and 'scroll the FlatList towards top'?
Minimal Example:
<View>
<FlatList/>
<TouchableOpacity>
<Text>Scroll towards Top</>Text
</TouchableOpacity>
<TouchableOpacity>
<Text>Scroll towards Bottom</>Text
</TouchableOpacity>
</View>

This is not difficult to accomplish, The <Flatlist/> component already have methods to do that.
scrollToEnd(): Scrolls to the end of the content.
scrollToIndex(): Scrolls to the item at the specified index such 0 which is the top.
I have created a simple demo for you: https://snack.expo.io/#abranhe/flatlist-scroll
I have created a custom <Button/> and <Card/> components. I am creating an array with some random data with this format
const data = [
{ message: 'Random Message' }, { message: 'Random Message' }
]
I am adding a reference to the <Flatlist/> by adding
ref={ref => (this.flatlist = ref)}
Then I call the methods and that's it.
<Button title="▼" onPress={() => this.flatlist.scrollToEnd()} />
The whole source code:
import React from 'react';
import { Text, View, FlatList, StyleSheet } from 'react-native';
import { random } from 'merry-christmas';
import Card from './components/Card';
import Button from './components/Button';
const data = [...Array(10)].map(i => ({ message: random() }));
export default () => (
<View style={styles.container}>
<FlatList
ref={ref => (this.flatlist = ref)}
data={data}
renderItem={({ item }) => <Card gretting={item.message} />}
/>
<View style={styles.bottomContainer}>
<Button
title="▲"
onPress={() => this.flatlist.scrollToIndex({ index: 0 })}
/>
<Button title="▼" onPress={() => this.flatlist.scrollToEnd()} />
</View>
</View>
);

You can use a scrollView component

Related

React Native: Sidebar is shown but unable to interact with it

I am using React Native Sidebar
This is the sidebar:
<Sidebar
ref={(ref) => this._drawer = ref}
leftSidebar={ this.renderLeftSidebar() }
leftSidebarWidth = {200}
>
</Sidebar>
This is how I'm rendering the left sidebar:
renderLeftSidebar = () =>{
return (
<View style = {{ position:'absolute', backgroundColor:'#24292e4f', height:Dimensions.get('window').height}}>
<DrawerContent/>
</View>
)
}
And this is the content for that left sidebar:
export default DrawerContent = () => {
return (
<View style={styles.animatedBox}>
<Image
source={require('../assets/images/header.png')}
style={{height:200, alignSelf:'center'}}
/>
<View style = {styles.drawerContentView}>
<Icon
name='setting'
type='antdesign'
color='#4abce3'
size ={22}
iconStyle = {styles.drawerItemIconStyle}
onPress={() => console.log('hello settings')}
/>
</View>
The sidebar opens just fine and renders correctly, but when I try and press anything on the sidebar the press goes through the sidebar and interacts with elements rendered beneath it. Should I be using zIndex in my styles or is my approach completely wrong?
If you are using React-native-vector-icons, the ICON itself has no button properties.
You can use Icon.Button
import Icon from 'react-native-vector-icons/AntDesign';
...
<Icon.Button
name='setting'
color='#4abce3'
size ={22}
iconStyle = {styles.drawerItemIconStyle}
onPress={() => console.log('hello settings')}
>
This is Setting
</Icon.Button>
OR You can use TouchableOpacity
import {
TouchableOpacity
} from 'react-native'
import Icon from 'react-native-vector-icons/AntDesign';
...
<TouchableOpacity onPress={() => console.log('hello settings')}>
<Icon
name='setting'
color='#4abce3'
size ={22}
iconStyle = {styles.drawerItemIconStyle}
/>
</TouchableOpacity>

Data not being displayed in one of two FlatLists in the same component

I have a component with two TextInputs. Under each TextInput, there is a FlatList which gets rendered when the user types in some characters (the actual rendering happens in ListItem, a module from react-native elements). For the first FlatList, everything works fine - it is rendered as expected. The problem is with the second FlatList - I cannot get it to render. See this gif to understand what I mean: https://gph.is/g/E1G1jnx
I've tried using the "extraData" prop as advised in One of two FlatLists not rendering items in same component , but it didn't fix the issue.
I know that the problem doesn not come from onChangeAddress(address) being an async function. I used a static dataset and still it wouldn't render.
import { StyleSheet, Text, TextInput, View, FlatList, ImageBackground, Image } from 'react-native';
import { ListItem, Button } from 'react-native-elements';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
jobInputValue: '',
addressInputValue: '',
showJobsDropdown: false,
showAddressesDropdown: false,
jobsList: this.props.jobTitles.results,
addressPredictions: []
};
}
async onChangeAddress(address) {
//fetch the data from the API
//filteredAddresses stores the data from the API
if (filteredAddresses.length > 0) {
this.setState({
addressPredictions: filteredAddresses,
showAddressesDropdown: true
})
}
}
}
render() {
return (
<ImageBackground style={styles.bkgImage} source={require('../assets/homepage_background.jpg')}>
<TextInput
style={styles.jobTextInput}
value={this.state.jobInputValue}
placeholder='Ce job cauți?'
onChangeText={(jobInputValue) => this.setState({jobInputValue}, this.checkJobsDropdown(jobInputValue))}/>
<FlatList
data={this.state.jobsList}
style={this.state.showJobsDropdown ? styles.jobsDropdownStyle : styles.dropdownHiddenStyle}
renderItem={({ item }) => (
<ListItem
title={item}
containerStyle={{paddingBottom: -4}}
titleProps={{ style: styles.dropdownItemStyle}}
onPress={() => this.setState({jobInputValue: item}, this.hideJobsDropdown())}
/>
)}
keyExtractor={item => item}
/>
<TextInput
style={styles.addressTextInput}
value={this.state.addressInputValue}
placeholder='La ce adresă locuiești?'
onChangeText={addressInputValue => this.onChangeAddress(addressInputValue)}
/>
//the issue is with the FlatList below
<FlatList
data={this.state.addressPredictions}
extraData={this.state}
style={this.state.showAddressesDropdown ? styles.addressDropdownStyle : styles.dropdownHiddenStyle}
renderItem={({ addressItem, index }) => (
<ListItem
title={addressItem}
containerStyle={{paddingBottom: -4}}
titleProps={{style: styles.dropdownItemStyle}}
onPress={() => this.setState({addressInputValue: addressItem}, this.hideAddressesDropdown())}
/>
)}
keyExtractor={(addressItem, index) => index.toString()}
/>
<Button
title="CAUTĂ"
type="outline"
underlayColor={colors.red}
titleStyle={styles.buttonTitleStyle}
color={colors.red}
style={styles.buttonStyle}
onPress={this.printState}
/>
</ImageBackground>
);
}
Any help will be greatly appreciated!
The problem is in the destructuring of the data for rendering the items of your second flatlist:
renderItem={({ addressItem, index }) => (
<ListItem
title={addressItem}
containerStyle={{paddingBottom: -4}}
titleProps={{style: styles.dropdownItemStyle}}
onPress={() => this.setState({addressInputValue: addressItem}, this.hideAddressesDropdown())}
/>
)}
({ addressItem, index }) <- there's no addressItem available here. You have to replace that with item, as Flatlist offers an object with the { item: Object, index: Number, separators: object } structure as the param to your renderList callback.

Conditionally style not working in react native

I followed this answer to dynamically style my component.
Here is my render method :
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.images}
numColumns={2}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
keyExtractor={item => item.localIdentifier}
renderItem={({ item, index }) =>
<TouchableHighlight
underlayColor='transparent'
onPress={() => this.openImage(index)}
onLongPress={() => this.startSelection(item)}
>
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
</TouchableHighlight>
}
/>
</View>
);
}
As you can see I am displaying image thumbnail with TouchableHighlight and FlatList. When user will press and hold on any image thumbnail I called startSelection() with particular flatlist item which then add that item to state. I used that state to set style dynamically of my image as :
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
Here is startSelection() method :
startSelection(item) {
let temp = this.state.selectedItems;
temp.push(item);
this.setState({
selectedItems : temp
});
}
Here is my stylesheet :
const styles = StyleSheet.create({
selectedItem: {
borderWidth: 3,
borderColor: '#22aaff',
},
unselectedItem: {
borderColor: '#000000',
}
});
But when user press and hold that view, item will added to state but style is not changing.
Please help me what's going wrong here !!!
This can be found on FlatList docs:
This is a PureComponent which means that it will not re-render if props remain shallow-equal. Make sure that everything your renderItem function depends on is passed as a prop (e.g. extraData) that is not === after updates, otherwise your UI may not update on changes. This includes the data prop and parent component state.
So you can add extraData to your FlatList component like this:
FlatList Component:
<FlatList
data={this.state.images}
extraData={this.state} //add this!
numColumns={2}
keyboardShouldPersistTaps={'always'}
keyboardDismissMode={'on-drag'}
keyExtractor={item => item.localIdentifier}
renderItem={({ item, index }) =>
<TouchableHighlight
underlayColor='transparent'
onPress={() => this.openImage(index)}
onLongPress={() => this.startSelection(item)}
>
<View style={[styles.albumContainer, (this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]}>
<Image
style={styles.albumThumbnail}
source={item.image}
/>
</View>
</TouchableHighlight>
}
/>
P.S: If your component state has variables which should not re-render FlatList, you would be better of using extraData = {this.state.selectedItems}, but then you need to make sure you pass a different reference to selectedItems when you call setState on startSelection. Like this:
startSelection(item) {
let temp = [...this.state.selectedItems];
temp.push(item);
this.setState({
selectedItems : temp
});
}
Wrap them with extra []
style={[styles.albumContainer, [(this.state.selectedItems.indexOf(item)>-1)?styles.selectedItem:styles.unselectedItem]]}

Click listener in flatlist

How can I add click listener in Flatlist?
My code:
renderItem({item, index}){
return <View style = {{
flex:1,
margin: 5,
minWidth: 170,
maxWidth: 223,
height: 304,
maxHeight: 304,
backgroundColor: '#ccc',
}}/>
}
render(){
return(<FlatList
contentContainerStyle={styles.list}
data={[{key: 'a'}, {key: 'b'},{key:'c'}]}
renderItem={this.renderItem}
/>);
}
}
Update 1: I used button but it is not working in Flatlist. However using only button instead of Flatlist, it works. Why is it not working in Flatlist renderItem?
_listener = () => {
alert("clicked");
}
renderItem({item, index}){
return<View>
<Button
title = "Button"
color = "#ccc"
onPress={this._listener}
/>
</View>
}
I used TouchableWithoutFeedback. For that, you need to add all the renderItem elements (i.e your row) into the TouchableWithoutFeedback. Then add the onPress event and pass the FaltList item to the onPress event.
import {View, FlatList, Text, TouchableWithoutFeedback} from 'react-native';
render() {
return (
<FlatList style={styles.list}
data={this.state.data}
renderItem={({item}) => (
<TouchableWithoutFeedback onPress={ () => this.actionOnRow(item)}>
<View>
<Text>ID: {item.id}</Text>
<Text>Title: {item.title}</Text>
</View>
</TouchableWithoutFeedback>
)}
/>
);
}
actionOnRow(item) {
console.log('Selected Item :',item);
}
You need to wrap your row element (inside your renderItem method) inside <TouchableWithoutFeedback> tag. TouchableWithoutFeedback takes onPress as it's prop where you can provide onPress event.
For TouchableWithoutFeedback refer this link
I used TouchableOpacity. and it's working great.This will give you click feedback. which will not be provided by TouchableWithoutFeedback. I did the following:
import { View, Text, TouchableOpacity } from "react-native";
.
.
.
_onPress = () => {
// your code on item press
};
render() {
<TouchableOpacity onPress={this._onPress}>
<View>
<Text>List item text</Text>
</View>
</TouchableOpacity>
}
If you are facing flatlist row first click issue
please add below property to flatlist.
disableScrollViewPanResponder = {true}
The Pressable component is now preferred over TouchableWithoutFeedback (and TouchableOpacity). According to the React Native docs for TouchableWithoutFeedback:
If you're looking for a more extensive and future-proof way to handle touch-based input, check out the Pressable API.
Example implementation:
import { Pressable } from "react-native";
render() {
return(
<FlatList
contentContainerStyle={styles.list}
data={[{key: 'a'}, {key: 'b'}, {key:'c'}]}
renderItem={({item}) => (
<Pressable onPress={this._listener}>
// BUILD VIEW HERE, e.g. this.renderItem(item)
</Pressable>
)}
/>
);
}
References
TouchableWithoutFeedback (React Native): https://reactnative.dev/docs/touchablewithoutfeedback
Pressable (React Native): https://reactnative.dev/docs/pressable
you dont need to add Touchable related component into your Flatlist renderItem. Just pass onTouchStart prop to your Flatlist.
in example:
<FlatList
style={themedStyles.flatListContainer}
data={translations}
renderItem={renderItem}
keyExtractor={(item, index) => `${item.originalText}____${index}`}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
ListEmptyComponent={renderEmptyListComponent}
onTouchStart={onBackgroundPressed}
/>

React Native: Refreshing gets stuck while scrolling in nesting FlatLists in Androdid

While using a horizontal FlatList with other vertical FlatList as items there seems to be an issue with the refreshing. This happens when refreshing is enabled on the vertical lists but not on the horizontal container list. It is actually possible to refresh each individual list if you are very carful and only scrolls vertically (this is very hard). But at once you scrolls sideways the refreshing gets stuck.
React Native nested FlatLists
Issue in Android. Works in iOS
Attempts
I have tried replacing the wrapping FlatList with a ScollView with the same result. I am fully aware of that it is possible to disable refreshing of the individual list and enable it on the containing FlatList but that is not very appropriate in my case.
I have also tried the upvoted answers on this similar question but it didn't solve it.
Example:
<FlatList
horizontal={true}
pagingEnabled={true}
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) =>
<FlatList
style={{width: 400}}
ref="scrollView"
horizontal={false}
refreshing={false}
onRefresh={() => {}}
data={[{key: 'c'}, {key: 'd'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
}
/>
Does anyone have a solution to this?
ScrollView/ Flatlist import from react-native-gesture-handler can be stuck refresh when released outside of the screen. Use ScrollView import from react-native
i created a component based on what i understood from ur question,
make second flat list width as u want and put the height as '100%' so it will com full screen, so that it behaves like paging 2 flat lists... Hope it works for u
Here is the code
Snack URL
import React, {Component} from 'react';
import {
View,
Text,
Image,
TouchableOpacity,
FlatList,
Dimensions,
} from 'react-native';
const { width } = Dimensions.get('window');
export default class App extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
horizontal
pagingEnabled
data={[{ key: 'a' }, { key: 'b' }]}
renderItem={({ item }) => (
<FlatList
style={{ width, height: '100%' }}
// ref="scrollView"
horizontal={false}
refreshing={false}
onRefresh={() => {}}
data={[{ key: 'c' }, { key: 'd' }, { key: 'f' }, { key: 'h' }]}
renderItem={({ item }) => (
<Text style={{ paddingVertical: 40 }}>{item.key}</Text>
)}
/>
)}
/>
</View>
);
}
}