Collapsible header in react-native-reanimated with multiple tabs in React Native

5.4k Views Asked by At

I'm trying to create a screen in React Native with 2 tabs (using react-native-tab-view) with the following layout:

 ------------------------
|   Collapsible Header   |
|------------------------|  
|    Tab A  |  Tab B     |
|------------------------| 
|                        |
|                        |
|        FlatList        |
|         in Tab         |
|         Content        |
|                        |
|                        |
|                        |
|                        |
 ------------------------

The layout I went for is absolute positioning for both the collapsible header AND the tab bar, and the FlatList actually covers the entire screen (and both the header and the tab bar are on top of it). To put the scrollable part after the tab bar, I added a paddingTop to the contentContainerStyle of the FlatList. This is the root of the issue - When scrolling in Tab A, then moving to Tab B, there will be a blank space because of that padding.

I created a complete Expo project to show this issue: https://expo.io/@bartzy/collapsible-tab-view-example This is the Github repo for the Expo project: https://github.com/bartzy/CollapsibleTabViewExample

Here's a quick video of the example app, showing the blank padding space after switching to Tab 2: enter image description here

I'd appreciate any idea on how to eliminate that blank space when moving between tabs, while keeping the desired collapsing behavior.

2

There are 2 best solutions below

2
On BEST ANSWER

This is a common problem, there are few examples around on how to solve this, but recently I published a wrapper for the react-native-tab-view to solve this issue.

Demo

Example

Example from the quick start.

import * as React from 'react';
import { StyleSheet, View, Text, Animated } from 'react-native';
import {
  CollapsibleTabView,
  useCollapsibleScene,
} from 'react-native-collapsible-tab-view';
import { SceneMap } from 'react-native-tab-view';

type Route = {
  key: string;
  title: string;
};

const SomeRoute: React.FC<{ routeKey: string; color: string }> = ({
  routeKey,
  color,
}) => {
  const scrollPropsAndRef = useCollapsibleScene(routeKey);

  return (
    <Animated.ScrollView
      style={{ backgroundColor: color }}
      {...scrollPropsAndRef}
    >
      <View style={styles.content} />
    </Animated.ScrollView>
  );
};

const FirstScene = () => <SomeRoute routeKey="first" color="white" />;
const SecondScene = () => <SomeRoute routeKey="second" color="black" />;

const HEADER_HEIGHT = 250;

const renderHeader = () => (
  <View style={styles.header}>
    <Text style={styles.headerText}>COLLAPSIBLE</Text>
  </View>
);

const renderScene = SceneMap({
  first: FirstScene,
  second: SecondScene,
});

const App: React.FC<object> = () => {
  const [index, setIndex] = React.useState(0);
  const [routes] = React.useState<Route[]>([
    { key: 'first', title: 'First' },
    { key: 'second', title: 'Second' },
  ]);

  const handleIndexChange = (index: number) => {
    setIndex(index);
  };

  return (
    <CollapsibleTabView<Route>
      navigationState={{ index, routes }}
      renderScene={renderScene}
      onIndexChange={handleIndexChange}
      renderHeader={renderHeader} // optional
      headerHeight={HEADER_HEIGHT} // optional, will be computed.
    />
  );
};

export default App;

const styles = StyleSheet.create({
  header: {
    height: HEADER_HEIGHT,
    backgroundColor: '#2196f3',
    justifyContent: 'center',
    alignItems: 'center',
    elevation: 4,
  },
  headerText: {
    color: 'white',
    fontSize: 24,
  },
  content: {
    height: 1500,
  },
});
0
On

You need to keep references for the flat list and scolltoOffset for the tab not in focus. https://gist.github.com/maitham/6e0841800d88bf9c289fc45bbc903b1d. This seems to work for me.