react-native-tab-view
react-native-tab-view copied to clipboard
Can we support a sticky tab bar to support render a header component for tab view
Description
I want to hide the header component while scroll the tab screen, but the tab bar need be sticky on the top area.
I believe there has many developers watching on this feature, because FlatList & ScrollView & SectionList all support a props stickyHeaderIndices to make specific sub component sticky.
But unfortunately, our Tab View here is not support
Alternatives
but I have a customized react-native-tab-view+2.16.0.patch for [email protected], it may be inelegant but useful for myself
diff --git a/node_modules/react-native-tab-view/lib/module/TabBar.js b/node_modules/react-native-tab-view/lib/module/TabBar.js
index b287a8a..6199247 100644
--- a/node_modules/react-native-tab-view/lib/module/TabBar.js
+++ b/node_modules/react-native-tab-view/lib/module/TabBar.js
@@ -10,6 +10,7 @@ import Animated from 'react-native-reanimated';
import TabBarItem from './TabBarItem';
import TabBarIndicator from './TabBarIndicator';
import memoize from './memoize';
+import { equals } from 'ramda'
const scheduleInNextFrame = cb => {
let frame = requestAnimationFrame(() => {
@@ -241,7 +242,7 @@ export default class TabBar extends React.Component {
const translateX = this.getTranslateX(this.scrollAmount, this.getMaxScrollDistance(tabBarWidth, layout.width));
return /*#__PURE__*/React.createElement(Animated.View, {
onLayout: this.handleLayout,
- style: [styles.tabBar, style]
+ style: style ?? styles.tabBar,
}, /*#__PURE__*/React.createElement(Animated.View, {
pointerEvents: "none",
style: [styles.indicatorContainer, scrollEnabled ? {
@@ -307,7 +308,7 @@ export default class TabBar extends React.Component {
this.measuredTabWidths[route.key] = e.nativeEvent.layout.width; // When we have measured widths for all of the tabs, we should updates the state
// We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
- if (routes.every(r => typeof this.measuredTabWidths[r.key] === 'number')) {
+ if (routes.every(r => typeof this.measuredTabWidths[r.key] === 'number') && Object.keys(this.measuredTabWidths).length === routes.length && !equals(this.state.tabWidths, this.measuredTabWidths)) {
this.setState({
tabWidths: _objectSpread({}, this.measuredTabWidths)
});
diff --git a/node_modules/react-native-tab-view/lib/module/TabView.js b/node_modules/react-native-tab-view/lib/module/TabView.js
index d0041ce..fce3358 100644
--- a/node_modules/react-native-tab-view/lib/module/TabView.js
+++ b/node_modules/react-native-tab-view/lib/module/TabView.js
@@ -7,7 +7,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import * as React from 'react';
-import { StyleSheet, View } from 'react-native';
+import { StyleSheet, View, ScrollView } from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import Animated from 'react-native-reanimated';
import TabBar from './TabBar';
@@ -72,7 +72,8 @@ export default class TabView extends React.Component {
style,
gestureHandlerProps,
springVelocityScale,
- renderPager
+ renderPager,
+ renderHeader = () => /*#__PURE__*/React.createElement(React.Fragment, null)
} = this.props;
const {
layout
@@ -110,7 +111,7 @@ export default class TabView extends React.Component {
};
return /*#__PURE__*/React.createElement(React.Fragment, null, positionListener ? /*#__PURE__*/React.createElement(Animated.Code, {
exec: Animated.set(positionListener, position)
- }) : null, tabBarPosition === 'top' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
+ }) : null, /*#__PURE__*/React.createElement(ScrollView, {stickyHeaderIndices: [1]}, renderHeader({ ...sceneRendererProps, navigationState }), tabBarPosition === 'top' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
navigationState
})), render(navigationState.routes.map((route, i) => {
return /*#__PURE__*/React.createElement(SceneView, _extends({}, sceneRendererProps, {
@@ -133,7 +134,7 @@ export default class TabView extends React.Component {
})));
})), tabBarPosition === 'bottom' && renderTabBar(_objectSpread(_objectSpread({}, sceneRendererProps), {}, {
navigationState
- })));
+ }))));
}
}));
}
@@ -152,7 +153,7 @@ _defineProperty(TabView, "defaultProps", {
springConfig: {},
timingConfig: {},
gestureHandlerProps: {},
- renderPager: props => /*#__PURE__*/React.createElement(Pager, props)
+ renderPager: props => /*#__PURE__*/React.createElement(Pager, props),
});
const styles = StyleSheet.create({
diff --git a/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts b/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
index a6cd166..356e4e8 100644
--- a/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
+++ b/node_modules/react-native-tab-view/lib/typescript/src/types.d.ts
@@ -49,4 +49,10 @@ export declare type PagerCommonProps = {
timingConfig: {
duration?: number;
};
+
+ renderHeader?: (
+ props: SceneRendererProps & {
+ navigationState: NavigationState<Route>;
+ }
+ ) => React.ReactNode;
};
diff --git a/node_modules/react-native-tab-view/src/TabBar.tsx b/node_modules/react-native-tab-view/src/TabBar.tsx
index 8f5d257..afb20e9 100644
--- a/node_modules/react-native-tab-view/src/TabBar.tsx
+++ b/node_modules/react-native-tab-view/src/TabBar.tsx
@@ -361,7 +361,7 @@ export default class TabBar<T extends Route> extends React.Component<
return (
<Animated.View
onLayout={this.handleLayout}
- style={[styles.tabBar, style]}
+ style={style ?? styles.tabBar}
>
<Animated.View
pointerEvents="none"
@@ -500,7 +500,7 @@ const styles = StyleSheet.create({
overflow: Platform.select({ default: 'scroll', web: undefined }),
},
tabBar: {
- backgroundColor: '#2196f3',
+ backgroundColor: 'transparent',
elevation: 4,
shadowColor: 'black',
shadowOpacity: 0.1,
diff --git a/node_modules/react-native-tab-view/src/TabView.tsx b/node_modules/react-native-tab-view/src/TabView.tsx
index fe8cf9b..79f42dd 100644
--- a/node_modules/react-native-tab-view/src/TabView.tsx
+++ b/node_modules/react-native-tab-view/src/TabView.tsx
@@ -5,6 +5,7 @@ import {
StyleProp,
ViewStyle,
LayoutChangeEvent,
+ ScrollView,
} from 'react-native';
import {
PanGestureHandler,
@@ -46,6 +47,11 @@ export type Props<T extends Route> = PagerCommonProps & {
style?: StyleProp<ViewStyle>;
gestureHandlerProps: React.ComponentProps<typeof PanGestureHandler>;
renderPager: (props: ChildProps<T>) => React.ReactNode;
+ renderHeader?: (
+ props: SceneRendererProps & {
+ navigationState: NavigationState<T>;
+ }
+ ) => React.ReactNode;
};
type State = {
@@ -126,6 +132,7 @@ export default class TabView<T extends Route> extends React.Component<
gestureHandlerProps,
springVelocityScale,
renderPager,
+ renderHeader = () => <></>,
} = this.props;
const { layout } = this.state;
@@ -170,6 +177,11 @@ export default class TabView<T extends Route> extends React.Component<
exec={Animated.set(positionListener, position)}
/>
) : null}
+ <ScrollView stickyHeaderIndices={[1]}>
+ {renderHeader({
+ ...sceneRendererProps,
+ navigationState,
+ })}
{tabBarPosition === 'top' &&
renderTabBar({
...sceneRendererProps,
@@ -208,6 +220,8 @@ export default class TabView<T extends Route> extends React.Component<
...sceneRendererProps,
navigationState,
})}
+
+ </ScrollView>
</React.Fragment>
);
},
Inspirations & examples
No response
Couldn't find version numbers for the following packages in the issue:
react-nativereact-native-tab-viewreact-native-pager-view
Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.
Hey! Thanks for opening the issue. The issue doesn't seem to contain a link to a repro (a snack.expo.dev link or link to a GitHub repo under your username).
Can you provide a minimal repro which demonstrates the issue? A repro will help us debug the issue faster. Please try to keep the repro as small as possible and make sure that we can run it without additional setup.
This would be awesome to have. On a current project this functionality would be so useful. Do you have any updates/plans for this?
BTW, thanks for the work you did. It's awesome!
Hello 👋, this issue has been open for more than a month without a repro or any activity. If the issue is still present in the latest version, please provide a repro or leave a comment within 7 days to keep it open, otherwise it will be closed automatically. If you found a solution or workaround for the issue, please comment here for others to find. If this issue is critical for you, please consider sending a pull request to fix it.
finally, I integrated
[email protected]with[email protected]
@jiyuan12354 Is this in a pull request?
Closing this, since it's supported in react-native-collapsible-tab-view