Showtime Tab View
A React Native component that supports a collapsible header and custom refresh control, powered by Reanimated v2 and GestureHandler V2.
showtime-profile.mp4
What
This is a React Native tab view component that wraps gestures and animations on top of react-native-tab-view. The source code can be found here. You can see this context on Twitter.
Features
- Collapsible header.
- Support for FlashList. *(see this)
- Custom refresh control.
- Bounce effect support on iOS.
- Support for iOS, Android, and Web.
- Zoom header when pulling to refresh. *(see this thread)
Installation
Before installing this package, you should first follow the installation instructions for:
FlashList is not a required dependency, but for optimal performance, it is recommended to use FlashList instead of the standard FlatList implementation
And then, you can install the package using the following command:
yarn add @showtime-xyz/tab-view
Examples
- Basic Example
- Zoom Effect with Pull-To-Refresh
- Showtime Profile Example
- ...more to come!
Usage
The API for this package is similar to react-native-tab-view, with extended props. A basic usage example is shown below:
import React, { useCallback, useState } from "react";
import { StatusBar, Text, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import { Route, TabView } from "@showtime-xyz/tab-view";
import { TabFlashList } from "./tab-flash-list";
const StatusBarHeight = StatusBar.currentHeight ?? 0;
const TabScene = ({ route }: any) => {
return (
<TabFlashList
index={route.index}
data={new Array(20).fill(0)}
estimatedItemSize={60}
renderItem={({ index }) => {
return (
<View
style={{
height: 60,
backgroundColor: "#fff",
marginBottom: 8,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>{`${route.title}-Item-${index}`}</Text>
</View>
);
}}
/>
);
};
export function Example() {
const [isRefreshing, setIsRefreshing] = useState(false);
const [routes] = useState<Route[]>([
{ key: "like", title: "Like", index: 0 },
{ key: "owner", title: "Owner", index: 1 },
{ key: "created", title: "Created", index: 2 },
]);
const [index, setIndex] = useState(0);
const animationHeaderPosition = useSharedValue(0);
const animationHeaderHeight = useSharedValue(0);
const renderScene = useCallback(({ route }: any) => {
switch (route.key) {
case "like":
return <TabScene route={route} index={0} />;
case "owner":
return <TabScene route={route} index={1} />;
case "created":
return <TabScene route={route} index={2} />;
default:
return null;
}
}, []);
const onStartRefresh = async () => {
setIsRefreshing(true);
setTimeout(() => {
console.log("onStartRefresh");
setIsRefreshing(false);
}, 300);
};
const renderHeader = () => (
<View style={{ height: 300, backgroundColor: "#000" }}></View>
);
return (
<TabView
onStartRefresh={onStartRefresh}
isRefreshing={isRefreshing}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
lazy
renderScrollHeader={renderHeader}
minHeaderHeight={44 + StatusBarHeight}
animationHeaderPosition={animationHeaderPosition}
animationHeaderHeight={animationHeaderHeight}
/>
);
}
API
... API documentation will be available soon.
Contributing
To learn how to contribute to this repository and understand the development workflow, please refer to the contributing guide.
Shoutout
Special thanks to @Daavidaviid for experimenting with the zoom header effect with pull-to-refresh.
License
MIT
Made with create-react-native-library