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:
I'd appreciate any idea on how to eliminate that blank space when moving between tabs, while keeping the desired collapsing behavior.
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.
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,
},
});