I have a FlatList and I'm implementing a custom pull to refresh, and the idea is to scroll it to a negative offset to reveal the animation underneath it upon release. Here's the code for my FlatList.
const flatListRef = useRef(null);
const handleRelease = () => {
flatlistRef.current.scrollToOffset({ y: -100 });
setTimeout(() => {
flatlistRef.current.scrollToOffset({ y: 0 });
}, 1000)
}
return (
<FlatList
data={data}
renderItem={({ item }) => {
return (
<View style={styles.row}>
<Text style={styles.text}>{item}</Text>
</View>
)
}}
onScroll={onScroll}
scrollEventThrottle={16}
onResponderRelease={handleRelease}
ref={flatListRef}
/>
)
Upon releasing, the FlatList should scroll to offset -100 to reveal the animation underneath, and then scrolls back up after 1 second. But what's happening is that it is scrolling to offset 0 (I could tell because I tried scrolling down immediately upon releasing, it will immediately try to scroll back up).
Is it possible to programmatically scroll the FlatList to a negative offset?
It looks you need to set scrollToOverflowEnabled to true to apply this behavior.
ScrollView(Flatlist inherits ScrollView Props )
PS:
Here is a different idea.
Maybe you can add a fixed height View if you wanna go to -100. (looks same)
And after time set the view close. (back to origin position?)
(If it doesn't work....)
------------------edit-----
<FlatList
data={data}
renderItem={({ item }) => {
return (
<View style={styles.row}>
<Text style={styles.text}>{item}</Text>
</View>
)
}}
onScroll={onScroll}
scrollEventThrottle={16}
onResponderRelease={handleRelease}
ref={flatListRef}
scrollToOverflowEnabled={true} // Just put in here
/>
Use offset instead of y as the parameter key for scrollToOffset as stated in the docs:
flatlistRef.current.scrollToOffset({ offset: -100 });
Related
Swiping gestures i.e. scrolling, pull to refresh, etc.
I'm pulling my hair out trying to make this work, I don't understand how apps like Facebook accomplish this so well. It seems like such a simple thing to implement but I cannot for the life of me figure it out.
FYI: I'm using a FlatList with Touchable components inside. I've been messing around with the FlatList props (on scroll, on scroll begin drag, on scroll end drag, etc) as well as the Touchable props (on press, on press in, on press delay in, etc).
What I want: On the Facebook app, the MOMENT I begin scrolling or pulling to refresh, tap feedback is disabled so it doesn't look like I clicked on a post. But at the same time the MOMENT I tap on a post, the tap feedback is super responsive. How is this done?
What I get: The moment I begin scrolling or pulling to refresh, the tap feedback is played even though I wanted to scroll/refresh. To fix this, I tried putting a pressDelayIn of 50ms. But now, quickly tapping on a post doesn't play the feedback.
App.js
export default function App() {
const [refreshing, setRefreshing] = useState(false);
const [posts, setPosts] = useState([
{
id: 1,
username: '#somedude',
body: 'This is the best app ever, wow.',
},
{
id: 2,
username: '#doggo',
body: 'Woof woof. Woof woof woof! Woof... Woof woof? Woof!',
},
]);
const onRefresh = () => {
setRefreshing(true);
setTimeout(() => setRefreshing(false), 1000);
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={posts}
renderItem={({ item }) => <Post post={item} />}
keyExtractor={item => item.id}
refreshing={refreshing}
onRefresh={onRefresh}
/>
</SafeAreaView>
);
}
Post.js
export const Post = ({ post }) => {
return (
<TouchableOpacity
activeOpacity={0.5}
onPress={() => console.log(`Press id ${post.id}`)}
>
<View style={styles.postPontainer}>
<View style={{ marginBottom: 5 }}>
<Text>{post.username}</Text>
</View>
<View style={styles.textContainer}>
<Text>{post.body}</Text>
</View>
</View>
</TouchableOpacity>
);
}
I definitely understand the frustration. I'd +1 ucup's suggestion to checkout react-native-gesture-handler. In the mean time, I disabled the TouchableOpacity while scrolling, and dialed back the delayPressIn and it seem to work pretty well. See what you think:
Add state to track canPress
export default function App() {
const [canPress, setCanPress] = useState(true); //
const [refreshing, setRefreshing] = useState(false);
...
Wire up to the FlatList
<SafeAreaView style={styles.container}>
<FlatList
data={posts}
onScrollBeginDrag={() => setCanPress(false)} //
onScrollEndDrag={() => setCanPress(true)} //
renderItem={({item}) => <Post post={item} canPress={canPress} />} //
keyExtractor={item => item.id}
refreshing={refreshing}
onRefresh={onRefresh}
/>
</SafeAreaView>
Then just wire up to your TouchableOpacity
<TouchableOpacity
disabled={!canPress} //
activeOpacity={0.5}
delayPressIn={50} //
onPress={() => console.log(`Press id ${post.id}`)}>
...
Good luck!
you can check this answer i think it will help you to resolve your problem.
I think pointerEvents is the property you need to handle this.
this property controls whether the View can be the target of touch events.
Reference 1
Reference 2
I have an issue with scroll view inside Swipe model using react-native-modals. The problem is in Android the scrolling is smooth and working perfectly. However, with iOS scrolling is with short amount only it scrolls for tiny amount then it stops meaning i have to repeat scrolling for 10 times over the screen to view the bottom with small contents only. This is the code for the model.
<Modal
propagateSwipe={true}
visible={this.props.visible}
useNativeDriver={true}
modalAnimation={
new SlideAnimation({
initialValue: 0, // optional
slideFrom: 'bottom', // optional
useNativeDriver: false, // optional
})
}
style={stylesSwipeCard.modal}
rounded={true}
modalStyle={[
stylesSwipeCard.customModal,
this.props.customModalStyle,
{maxHeight: height * this.props.height},
]}
onTouchOutside={() => this.props.closeModal(false)}
swipeDirection={['down']} // can be string or an array
swipeThreshold={100} // default 100
onSwipeOut={(event) => {
this.props.closeModal(false);
}}
containerStyle={stylesSwipeCard.modalContainer}
footer={this.props.hasFooter && this.props.footerContent}>
<ModalContent style={stylesSwipeCard.modalContent}>
<View
style={[stylesSwipeCard.container, this.props.modalContainerStyle]}>
<Image source={line_swipe} style={{alignSelf: 'center'}} />
<ScrollView>
<TouchableHighlight>
<TouchableWithoutFeedback>
<View>{this.props.children}</View>
</TouchableWithoutFeedback>
</TouchableHighlight>
</ScrollView>
</View>
</ModalContent>
I would appreciate any help or suggestions 😄. Thanks
I have a horizontal ScrollView with paging enabled and each page is vertical FlatList.
Often, when users try to scroll down on FlatList, horizontal scroll happens and they end up on next ScrollView page.
Is there a way to configure ScrollView sensitivity so it triggers only when horizontal drag is larger, if that makes sense?
I've already looked into documentation, but haven't found anything.
<ScrollView
ref={swiperRef}
horizontal={true}
decelerationRate={'normal'}
snapToInterval={screenWidth}
snapToAlignment={'center'}
pagingEnabled={true}
disableIntervalMomentum={true}
>
{categories.map((category, index) => {
return (
<View key={category?.code}>
<FlatList
data={categories[index]}
renderItem={renderBox}
keyExtractor={(item, index) => index}
contentContainerStyle={styles.categoryNewsContainer}
onEndReached={({ distanceFromEnd }) => {
loadMore()
}}
onEndReachedThreshold={0.1}
onRefresh={() => refresh()}
refreshing={isRefreshing}
/>
</View>
)
})}
</ScrollView>
I had a similar problem, but with the vertical scrolling being too sensitive. This was my solution in native Android, so maybe it could help:
https://github.com/gavingt/upcoming-games/blob/master/app/src/main/java/com/gavinsappcreations/upcominggames/ui/detail/NestedScrollingParentScrollView.kt
You can just user decelerationRate for speed either "fast"/"normal" or just any float
Hy, I'm creating a slider in react-native. It works perfectly. It having three slides on scrolling. I done this using Animated.FlatList and scrollX. But One thing I'm not getting is how I can know which slide is currently displaying. I want to because it onboard screen I want to display the skip button on the first two slides and on the third I want to display something else button how I can do that.
const OnBoardScreen = () => {
const scrollX = useRef(new Animated.Value(0)).current;
// flatlist render function
const renderSplash = ({ item, index }) => {
return (
<View style={styles.mainWrapper}>
<View style={styles.imageWrapper}>{item.image}</View>
<View style={[styles.titleWrapper,
{marginTop:index==1 ? hp(4):index == 2 ? hp(10):null}
]}>
<Text style={styles.titleText}>{item.title}</Text>
</View>
<View style={styles.bodyWrapper}>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
</View>
);
};
return (
<View style={styles.container}>
{/* background Svgs */}
<View style={styles.blueSvgWrapper}>
<BlueSvg />
</View>
<View style={styles.dotSvgWrapper}>
<DotsSvg />
</View>
<View style={styles.orangeSvgWrapper}>
<OrangeSvg />
</View>
{/* Main Content */}
<Animated.FlatList
data={onBoardData}
keyExtractor={(item, index) => item.key}
renderItem={renderSplash}
horizontal
scrollEventThrottle={32}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: false }
)}
pagingEnabled
showsHorizontalScrollIndicator={false}
/>
<Indicator scrollX={scrollX} />
{skip == false ?
<TouchableOpacity style={styles.skipWrapper} >
<Text style={styles.skipText} >Skip</Text>
</TouchableOpacity>
: null }
</View>
);
};
I try to do conditions on scrollX but it's not updating the value like states do.
My Issue:
How to know apply condition on slides which thing should be show or which not?
FlatList inherits all of ScrollView props.
Therefore you can use onScroll.
onScroll = (event) => {
const scrollOffset = event.nativeEvent.contentOffset.y
}
I have a very frustrating situation. Trying to get keyboard to disappear and detect onPress event handler in child row.
Here is what my code looks like:
_renderRow = (prediction) => {
return (
<TouchableOpacity onPress={() => {
Keyboard.dismiss();
this.setState({ location: prediction.description });
}}>
<View style={styles.listItemContainer}>
<Text>{prediction.description}</Text>
</View>
</TouchableOpacity>
)
}
render() {
return (
<View style={styles.wrapper}>
{/* style={[this.state.predictions.length > 0 ? styles.searchContainerSuggest : styles.searchContainer]} */}
<View style={styles.searchContainerSuggest}>
<View style={{paddingLeft: 10, height: 45, display: 'flex', justifyContent: 'center'}}>
<TextInput
placeholder="Enter location"
value={this.state.location}
onChangeText={location => this.onChangeLocation(location)}
style={styles.textInput}
/>
</View>
{this.state.predictions.length && this.state.location !== '' ?
<FlatList
keyboardShouldPersistTaps={'handled'}
refreshing={!this.state.loaded}
initialNumToRender={10}
enableEmptySections={true}
data={this.state.predictions}
keyExtractor={(_, index) => index.toString()}
renderItem={ ({item: prediction}) => this._renderRow(prediction) } />
: null}
</View>
</View>
);
}
I probably need a helping hand or two with regards to how to debug this issue.
Looked up several examples on how to deal with hiding the keyboard and allowing a particular selection to be pressed at the same time.
I thought that keyboardShouldPersistTaps would allow for the child selection to be selected. Upon selection, the onPress event handler will trigger and that will be where I call Keyboard.dismiss() to hide the keyboard. Does not seem to work.
In my case, besides adding keyboardShouldPersistTabs='handled' to the FlatList in question, it was also needed to add keyboardShouldPersistTabs='handled' and nestedScrollEnabled={true} to a parent ScrollView like 2 levels above, wrapping the FlatList I intended to get this behavior with. Check out this issue in react-native repo for more info.
For anyone who is running into the same problem as me. Check whether your FlatList or ScrollView is nested in another FlatList or ScrollView.
If yes, then add
keyboardShouldPersistTaps='handled'
to the element as a props as well.
add keyboardDismissMode="none" to FlatList