Skip to content

Instantly share code, notes, and snippets.

@yasaminyaldaei
Created June 4, 2021 16:20
Show Gist options
  • Save yasaminyaldaei/7ceef4ad185065b1a796ab1f19ac0f17 to your computer and use it in GitHub Desktop.
Save yasaminyaldaei/7ceef4ad185065b1a796ab1f19ac0f17 to your computer and use it in GitHub Desktop.
React Native Collapsible Header with Sticky Bar using Animated API
import React from 'react';
import {Animated, StyleSheet} from 'react-native';
class CollapsibleHeader extends React.Component {
state = {
layoutHeight: 0,
clampedScroll: 0,
stickyHeight: this.props.stickyHeaderHeight,
};
componentDidUpdate() {
const {scrollY, stickyHeaderHeight} = this.props;
const {layoutHeight, clampedScroll} = this.state;
if (stickyHeaderHeight && layoutHeight && !clampedScroll) {
this.setState({
clampedScroll: Animated.diffClamp(
scrollY,
0,
layoutHeight - stickyHeaderHeight,
),
});
}
}
setStickyHeight = (stickyHeight) => {
this.setState({
stickyHeight,
});
};
onLayout = ({
nativeEvent: {
layout: {height},
},
}) => {
this.setState({
layoutHeight: height,
});
this.props.onLayout && this.props.onLayout(height);
};
render() {
const {clampedScroll, layoutHeight, stickyHeight} = this.state;
const translateY =
clampedScroll && layoutHeight && stickyHeight
? Animated.multiply(clampedScroll, -1)
: 0;
return (
<Animated.View
style={[styles.container, {transform: [{translateY}]}]}
onLayout={this.onLayout}>
{this.props.children}
</Animated.View>
);
}
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: 'black',
zIndex: 10,
},
});
export default CollapsibleHeader;
import React, {createRef, Component} from 'react';
import {Animated, Text, StyleSheet} from 'react-native';
import ListItem from '../Components/ListItem';
import CollapsibleHeader from '../Components/CollapsibleHeader';
import TabBar from '../Components/TabBar';
class Home extends Component {
scrollY = new Animated.Value(0);
header = createRef(null);
state = {
headerHeight: 0,
stickyHeaderHeight: 0,
data: [],
};
componentDidMount() {
// Fetching data
}
onHeaderLayout = (headerHeight) => {
this.setState({
headerHeight,
});
};
onStickyHeaderLayout = (stickyHeaderHeight) => {
this.setState({
stickyHeaderHeight,
});
this.header?.current?.setStickyHeight(stickyHeaderHeight);
};
render() {
const {stickyHeaderHeight, data} = this.state;
return (
<>
<Animated.FlatList
contentContainerStyle={{paddingTop: this.state.headerHeight}}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: this.scrollY}}}],
{useNativeDriver: true},
)}
data={data}
renderItem={({item}) => (
<ListItem name={item.name} avatar={item.avatar} id={item.id} />
)}
keyExtractor={(item) => item.id}
/>
<CollapsibleHeader
ref={this.header}
onLayout={this.onHeaderLayout}
scrollY={this.scrollY}
stickyHeaderHeight={stickyHeaderHeight}>
<Text style={styles.sectionTitle}>My Awesome App</Text>
<TabBar onLayout={this.onStickyHeaderLayout} />
</CollapsibleHeader>
</>
);
}
}
const styles = StyleSheet.create({
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: 'white',
margin: 20,
},
});
export default Home;
import React from 'react';
import {View, Text, StyleSheet, FlatList, TouchableOpacity} from 'react-native';
const TABS = [
{
title: 'Section 1',
key: 0,
},
{
title: 'Section 2',
key: 1,
},
{
title: 'Section 3',
key: 2,
},
];
class TabBar extends React.Component {
state = {
selected: 0,
};
onViewLayout = ({
nativeEvent: {
layout: {height, y},
},
}) => {
const {onLayout} = this.props;
onLayout && onLayout(height, y);
};
onTabChange = (selected) => {
this.setState(
{
selected,
},
() => {
const {onChange} = this.props;
onChange && onChange(this.state.selected);
},
);
};
render() {
const {selected} = this.state;
return (
<View style={styles.container} onLayout={this.onViewLayout}>
<FlatList
horizontal
data={TABS}
keyExtractor={(item) => '' + item.key}
renderItem={({item, index}) => (
<TouchableOpacity onPress={() => this.onTabChange(index)}>
<Text
style={[
styles.tab,
selected === index ? styles.selectedTab : null,
]}>
{item.title}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 20,
alignItems: 'baseline',
backgroundColor: 'black',
},
tab: {
fontSize: 14,
color: 'white',
padding: 10,
marginRight: 10,
},
selectedTab: {
borderBottomWidth: 3,
borderBottomColor: 'white',
},
});
export default TabBar;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment