I have a Flatlist inside a ScrollView with 100 items, but no matter what height I give to the ScrollView or Flatlist, there are always just 18 items displayed on my Phone. After that the slider of the ScrollView is still scrolling down, but the list ends after 18 items. I want to be able to scroll through the complete list of items with the ScrollView containing the FlatList. I am using a Samsung Galaxy S8+ as a test device.
Here is a snack of the problem: https://snack.expo.io/#christophhummler/stickyheaderscrollscreens
Thanks for your help :)
I think it has to do with the scrollEnabled={false}, set it to true or remove the prop altogether otherwise it may be your todos array
Edit
Since you want to create a sticky header you must set the height to be the device height of screen + height of header, change styles.scrollView to :
scrollView: {
height: height + 200,
}
also on android there is an issue of nesting scrollviews, to enable a nested scrollview add the nestedScrollEnabled prop:
<FlatList
...
nestedScrollEnabled={true}
/>
You will have to implement logic to enable the Flatlist scroll view when the specific threshholds are met. It works at the moment in conjunction with scrollEnabled={true}
i hope this helps
Related
I have a ScrollView containing several graphs made with react-native-skia. The graphs are interactable, i.e. I can touch them and move an indicator on the graph along the x-axis of the graph.
My issue is that the whenever the ScrollView events fire (i.e. we scroll up/down), then the graph touch events are ignored which makes for bad UX.
Demo:
Here's a Snack with a reproducible demo: https://snack.expo.dev/#jorundur/supportive-raisins
FYI: For some reason for me the Canvas disappears after 1 second in the Snack, but if you make any change in the file and save (such add a newline somewhere), then it appears. It's fine if you run this locally. But that's some other problem we can ignore for the purpose of this discussion.
Description:
The demo is a react-native ScrollView large enough to make sure we have to scroll, the top component is a graph using react-native-skia. If we drag the cursor in the graph around, then the UX gets bad pretty quickly as the graph touch events seem to be ignored as soon as any vertical scrolling happens. I've tried playing around with ScrollView from react-native-gesture-handler but without luck.
To me, the expected behaviour would be for the graph to be interactable even while scrolling. I.e. if I'm pressing the graph and move my finger diagonally up/down I would expect the ScrollView to scroll and the graph cursor also to change its position accordingly. (I say diagonally since a straight vertical movement wouldn't change the cursor position in this graph.)
Much appreciated if anyone has any ideas on how to solve this! I couldn't work out how to do it via GestureDetector from react-native-gesture-handler like I've heard suggested.
Possible solution (?):
What I think I need to do is remove the onTouch={touchHandler} which I'm using currently in the react-native-skia Canvas component and instead get those touches via gesture detection from react-native-gesture-handler. I then need to make those gestures work simultaneously with the parent ScrollViews scroll event gestures. I've not had any luck implementing that unfortunately.
The solution was to do the following:
Don't use onTouch on Canvas, instead detect gestures via react-native-gesture-handler
Create a gesture and add a ref to it
Add the simultaneousHandlers prop to the ScrollView and use the ref there
This tells the ScrollView that its events should not be blocked by the touch events from the ref
To reiterate, what I wanted to do was to have the touch events of a ScrollView work simultaneously with touch events from a react-native-skia Canvas child component.
Code (relevant bits):
import * as React from 'react';
import {
GestureHandlerRootView,
ScrollView // Note that this is not imported from react-native
} from 'react-native-gesture-handler';
const App = () => {
// Create ref for the ScrollView to know what other gestures it should work simultaneously with.
// My use case required pan but this can be swapped for other gestures.
const panGestureRef = React.useRef<GestureType>(Gesture.Pan());
// The logic that used to be in `onTouch` on the Canvas is now here
const panGesture = Gesture.Pan()
.onChange((e) => {
// Do stuff, also use the other callbacks available as needed
})
.withRef(panGestureRef);
return (
<GestureHandlerRootView style={{ flex: 1 }}>{/* Don't forget this! It should be at the root of your project. */}
<ScrollView simultaneousHandlers={[panGestureRef]}>
<GestureDetector gesture={panGesture}>
<Canvas style={{ width: 200, height: 200 }}>
{/* Your interactive react-native-skia magic */}
</Canvas>
</GestureDetector>
</ScrollView>
</GestureHandlerRootView>
);
};
export default App;
I'm having a weird behavior here and I can't figure out what's going on. I have a ScrolView with a form and some inputs and labels inside and it seems to only want to scroll when you place your finger (or cursor in the demo) over an input or a switch and it doesn't scroll when placing your finger anywhere else in the ScrollView
I put together an Expo Snack to show the code and if you run in the emulator and attempt to scroll the ScrollView by placing the cursor over one of the labels or on the edges of the ScrollView it won't scroll but if you place the cursor over an input or a switch it scrolls just fine
https://snack.expo.io/#jordanr/weird-scrollview-bahavior
The issue is because Touchable effects in your TouchableWithoutFeedback are blocking the effects of ScrollView, therefore you need to reset your responder by wrapping the Content inside the View
<Content>
<View onStartShouldSetResponder={() => true}>
//... Rest of the code
</View>
</Content>
Also don't use ScrollView since NativeBase uses KeyboardAwareScrollView from the package react-native-keyboard-aware-scroll-view in the <Content/>
I am trying to make it so whenever something new is rendered into a scroll view, the scroll view will stay put and not bump up and down. Right now if a new component is rendered in, the scrollview appears to be reset to 0.
Is there a way to stop this behavior, and hold position?
Right now for the scrollview I am using:
handleScrollt(event){
this.scroll = event.nativeEvent.contentOffset.y
}
handleSizet(width, height){
if (this.scroll) {
const position = this.scroll + height - this.height
this.refs.sv.scrollTo({x: 0, y: position, animated: false})
}
this.height = height
}
<ScrollView
ref="sv"
scrollEventThrottle={16}
onScroll={this.handleScrollt.bind(this)}
onContentSizeChange={this.handleSizet.bind(this)}
The issue with this is the scrollview will render briefly, before then scrolling to the correct offset. So it seems like theres a brief splash of the top of the screen
you have to define a height for the scrollview.
If the scrollview is supposed to cover an entire View you can get the view's height by:
<View style={styles.contactView} onLayout={(event) => {
var contactViewY = event.nativeEvent.layout.y;
this.setState({contactViewY: contactViewY})
}}>
and then give it to the scrollview
<ScrollView style={[styles.contactScroller, {height: this.state.contactViewY}]}>
Bear in mind that the onLayout method is called immediately once the layout has been calculated, so if the view's height changes later, this two lines of code alone won't update it.
You should try maintainVisibleContentPosition.
Example:
<ScrollView
{...props}
maintainVisibleContentPosition={{
minIndexForVisible: 0,
}}
/>
From the docs:
maintainVisibleContentPosition
When set, the scroll view will adjust the scroll position so that the
first child that is currently visible and at or beyond
minIndexForVisible will not change position. This is useful for lists
that are loading content in both directions, e.g. a chat thread, where
new messages coming in might otherwise cause the scroll position to
jump. A value of 0 is common, but other values such as 1 can be used
to skip loading spinners or other content that should not maintain
position.
Consider this simple ScrollView.
On iOS, clicking on the text will but the text to the top because scrollTo({ y: 250 }) scrolls even if end of scrollView is reached.
On Android, the text doesn't move.
How to get on Android the same behavior we have on iOS?
You can work around this by adding padding to the contentContainerStyle of the ScrollView. A good starting point would be to add padding equal to the height of the device's screen.
import { Dimensions } from 'react-native';
const ANDROID_SCREEN_HEIGHT_PADDING = Dimensions.get('window').height;
then, when rendering:
<ScrollView
contentContainerStyle={{paddingBottom: ANDROID_SCREEN_HEIGHT_PADDING}}>
...
</ScrollView>
You could use this for necessary extra Android padding in every direction. If you are using horizontal={true}, for example, you could add padding equal to the width of the screen and add it to the paddingLeft style property to get the intended scrolling behavior on Android.
This behavior is due to the underlying implementation of ScrollView on Android in React-Native. See also:
React native Android ScrollView scrollTo not working
Is it possible to have a layout which is wrapped with ScrollView:
<ScrollView>
<View>dynamic height</View>
<View>flex with minHeight</View>
<View>static height</View>
</ScrollView>
and meets below prerequisites:
Height of the first View is dynamic (depends on text length). Height of the third View is static (always three buttons inside). Rest of the screen should be filled with View with map, but the minimal height is set to 250.
Now the tricky part: if there is a lot of text in first View, so that map doesn't fit, a scroll should appear. I couldn't achieve that. I was trying to give the map View flex: 1 and minHeight: 250, but it's not rendered at all.
Solution
Ok, I've found a way to fix it. In first render, I get screen height (Dimensions) and height of first and third view (onLayout). Then, I calculate the height of second view (screenHeight - view1 - view3 - naviagtionHeight) and forceUpdate() to re-render it.
Add contentContainerStyle={{flexGrow: 1}} prop to ScrollView and its children's flex:1 would work. So you don't need to calculate it manually.