'The best way to create a scrollable tab in the middle of the screen?

enter image description here

The mobile app of Twitter has a scrollable tab in the middle of the screen when you are on your profile. The top half of the screen displaying your profile info etc doesn't change when you click on the scrollable tabs mid screen : "Tweets & replies", "Media" etc. I am wondering how one would create this? Having half the screen stay the same and then having tabs which change mid screen... At the moment I have react navigation tabs as my main navigation - so on one of these tabs (the profile tab) I want to create the same concept as the picture..



Solution 1:[1]

Late answer but (for anyone else and future reference), react-navigation uses this package, react-native-tab-view: https://github.com/react-native-community/react-native-tab-view

for their tabs.

You can nest this within a screen, just like you desire (the previous answer only addresses the navigator inside navigator and that isn't what you want).

Here is an example (not exactly like you want, but you get the idea that you can. so instead of a background image, swap it out and use a view or scrollview accordingly to create that layout):

https://snack.expo.io/@satya164/collapsible-header-with-tabview

cheers :)

EDIT: i just found a way with just using react-navigation after all:

https://snack.expo.io/@mattx/collapsible-header-tabs

check it out

and another library: https://github.com/benevbright/react-navigation-collapsible

Solution 2:[2]

I don't know if you've figured it out yet, but you can nest the TabNavigator inside a StackNavigator. That way, you can have a scrollable Tab.

    class ProfileMenu extends React.Component{
    render() {
     return(
      //whatever you wanted at the top 
      )
     }
    }
    
    const TabNaviga = createMaterialTopTabNavigator({
      Tweets: {screen: TweetScreen,},
      Replies: {screen: RepliesScreen,},
    })
    
    const YNavigator = createStackNavigator ({
      Home:{screen: TabNaviga,
        navigationOptions: ({navigation}) => ({
          header: <ProfileMenu navigation= {navigation} />,
        })
      }
    })
export default YNavigator

Solution 3:[3]

I found this tutorial and followed it,

EDIT: it seems there's a new library out that supports it https://github.com/PedroBern/react-native-collapsible-tab-view

https://medium.com/@linjunghsuan/implementing-a-collapsible-header-with-react-native-tab-view-24f15a685e07

I also wrote a bit of an explaination if you are interested.

create a ScrollY with useRef and .current at the end

create a handleScroll function which returns an event like so -

const handleScroll = Animated.event(
  [{ nativeEvent: { contentOffset: { y: scrollY } } }],
  { useNativeDriver: true }
);

Pass it down in props to the wanted component

<TabNavigator handleScroll={handleScroll} scrollY={scrollY} />

And also the scrollY so you can use the value in the Child component aswell

pass it farther down the line to actual events like and call handleScroll in the Child Child component onScroll prop. like so

<Animated.FlatList
  ...
  onScroll={handleScroll}
/>

And now you can use the ScrollY value wherever you want.

what it does is checking if the current route is the one we check, it then caluclates the offset and scrollToOffset function of flatlist using the flatlist refs we got from here

return (
  <Pictures
    handleScroll={handleScroll}
    onMomentumScrollBegin={onMomentumScrollBegin}
    onScrollEndDrag={onScrollEndDrag}
    onMomentumScrollEnd={onMomentumScrollEnd}
    onGetRef={ref => {
      if (ref) {
        const found = listRefArr.current.find(e => e.key === route.key);
        if (!found) {
          listRefArr.current.push({ key: route.key, value: ref });
        }
      }
    }}
  />
);

the onGetRef is connected to the FlatList ref

return (
  <AnimatedFlatList
    ref={onGetRef}
    scrollToOverflowEnabled
    onMomentumScrollBegin={onMomentumScrollBegin}
    onScrollEndDrag={onScrollEndDrag}
    onMomentumScrollEnd={onMomentumScrollEnd}
    onScroll={handleScroll}
    scrollEventThrottle={16}
    contentContainerStyle={{
      paddingTop: HeaderHeight + TabBarHeight,
      paddingHorizontal: 10,
      minHeight: windowHeight - TabBarHeight
    }}
    data={data}
    renderItem={({ item }) => {
      return <Comment data={item} />;
    }}
    keyExtractor={({ commentId }): any => {
      return commentId.toString();
    }}
  />
);

then we have these three functions which we send the flatlist as well

const onMomentumScrollBegin = () => {
  isListGliding.current = true;
};
const onMomentumScrollEnd = () => {
  isListGliding.current = false;
  syncScrollOffset();
};
const onScrollEndDrag = () => {
  syncScrollOffset();
};

and last but not least we still need to animate the TabBar so when the header is 500 height his is 0 when the header is 450 in the y the tabbar should be 50, we do that by getting the scrollY in the props and use it to interpolate.

const renderTabBar = (props: any) => {
  return (
    <Animated.View
      style={{
        top: 0,
        zIndex: 1,
        position: "absolute",
        transform: [{ translateY: tabViewHeight }],
        width: "100%"
      }}
    >
      <TabBar ... />
    </Animated.View>
  );
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Sule
Solution 3 zhirzh