Bondan Herumurti
Bondan Herumurti

Reputation: 1018

Sticky Component inside scrollview

I'm trying to build a a sticky component like this app

http://www.screencapture.ru/file/E88F08Fc

Deals, Products, Events tabs/segmentControl are actually start from bottom and as you scroll when you hit bottom of the header it stops and start stick while the content keep scrolled

this is my code

        <View style={styles.container}>
            <ScrollView 
                style={styles.container}
                scrollEventThrottle={16}
                onScroll={
                    Animated.event(
                        [{nativeEvent:{contentOffset: {y: this.state.scrollY}}}]
                    )
                }
            >
                {this._renderScrollViewContent()}
            </ScrollView>
            <View style={styles.stickyStuff}></View>
            <Animated.View
                style={[styles.header, {height: headerHeight}]}
            >
                <Animated.Image
                    source={require('../../assets/images/pvj.jpg')}
                    style={[styles.backgroundImage, {opacity: imageOpacity}]}
                />
                <Animated.View style={[styles.bar, {transform: [{translateY: titleTranslateY}, {translateX: titleTranslateX}]}]}>
                    <Text style={styles.title}>PARIS VAN JAVA</Text>
                </Animated.View>

            </Animated.View>
        </View>

Upvotes: 58

Views: 129522

Answers (5)

crispengari
crispengari

Reputation: 9343

This is very easy you just need to know the index component that you want it to sticky during scrolling inside a scrollview component. Here is an example:

     <ScrollView
          style={styles.screen}
          stickyHeaderIndices={[0]}
     >
         <View><Text>Hello1</Text></View>
         <View><Text>Hello2</Text></View>
         <View><Text>Hello3</Text></View>
     </ScrollView>

So when scrolling Hello1 text will sticky to the top of the ScrollView

Good Luck

Upvotes: 14

moto
moto

Reputation: 940

For people trying to have a sticky spacer over the status bar, you can use an intermediate element as a placeholder.

      const insets = useSafeAreaInsets();

      return (<ScrollView stickyHeaderIndices={[1]}>
        <HeroImage />

        <View style={{ paddingTop: insets.top, backgroundColor: 'white' }} />

        <View style={{ backgroundColor: 'white' }}>
          .... Your components
        </View>
      </ScrollView>)

The second view is what pads the top.

Upvotes: 0

Andrew
Andrew

Reputation: 5284

In some cases, you'll actually want to just position your element outside of the ScrollView and use position: absolute to achieve a sticky effect. This is often more straightforward, if the element is consistently supposed to stick at the top or bottom, and doesn't involve any styling issues, which stickyHeaderIndices can sometimes cause.


<MyComponent style={{position: absolute;}}/> // will be sticky to top
<ScrollView> 
    // scrolling components
</ScrollView>
<OtherComponent /> // functionally sticky to bottom; looks like content is scrolling behind it, even though the scrollview just stops before it.

Upvotes: 0

Bondan Herumurti
Bondan Herumurti

Reputation: 1018

Thank you for answering my first question. I found it how to do it now. So basically what I do is I cheat on F8App code where they fallback from F8Scrolling native module if it is not available to this:

if (!NativeModules.F8Scrolling) {
  var distance = EMPTY_CELL_HEIGHT - this.state.stickyHeaderHeight;
  var translateY = 0; this.state.anim.interpolate({
    inputRange: [0, distance],
    outputRange: [distance, 0],
    extrapolateRight: 'clamp',
  });
  transform = [{translateY}];
}

which gives the idea of animating my sticky view so here is i ended up

const stickySegmentControlX = this.state.scrollY.interpolate({
    inputRange: [0, STICKY_SCROLL_DISTANCE],
    outputRange: [INIT_STICKY_HEADER, HEADER_MIN_HEIGHT],
    extrapolate: 'clamp'
})

...

    return(
    ....
     <Animated.View ref="stickyHeader" style={[styles.stickyStuff, {top: stickySegmentControlX}]}>
       <Text>Go Stick</Text>
     </Animated.View>
    ....
   );

so basically what I've done is animating the top position of an {position: 'absolute'}'s view while as the value of output range is between bottom position to the height of my header (so it'll stop right below my header) and the input range is between 0 and the difference of the top position and the header height. Eventually, the animation will feel natural as you scroll the scrollview.

There you go a sticky header custom view. Here is the result:

enter image description here

If you feel my answer is confusing, its better you heading to janic duplessis Medium post:
React Native ScrollView animated header

Upvotes: 25

Muhammad Riyaz
Muhammad Riyaz

Reputation: 2942

It is very simple with ScrollView component. There is already something called "stickyHeaderIndices" which takes the index of child to make sticky.In following code, content inside renderComponent2 stays sticky when you scroll.

<ScrollView
      stickyHeaderIndices={[1]}
      showsVerticalScrollIndicator={false}
 >
  {this.renderComponent1()}
  {this.renderComponent2()}
  {this.renderComponent3()}
 </ScrollView>

Refer: https://facebook.github.io/react-native/docs/scrollview.html#stickyheaderindices

Upvotes: 134

Related Questions