I'm a newbie in React Native and trying to make a sticky tab view with react-native-tab-view
. What I want to achieve is like Twitter Profile UI.
Here's what I've made so far:
What I want to achieve is when the user scroll down, the tab bars will stick to the animated header. And when users scrolls up again, the tab will slowly come back to previous position.
I've tried many ways to do this but I couldn't wrap my head around it very well.
Here's the code
import ...
const WIDTH = Dimensions.get('screen').width;
const initialLayout = WIDTH;
const HEADER_MAX_HEIGHT = 120;
const HEADER_MIN_HEIGHT = 70;
const PROFILE_IMAGE_MAX_HEIGHT = 80;
const PROFILE_IMAGE_MIN_HEIGHT = 40;
const imgData = [
// ...
];
const NUM_COLUMNS = 3;
const ProfileScreen = (props) => {
const [index, setIndex] = useState(0);
const [isFollowed, setIsFollowed] = useState(false);
const [enableScrollView, setEnableScrollView] = useState(false);
const [routes] = React.useState([
{ key: 'post', title: 'Posts'},
{ key: 'trip', title: 'Blogs'},
]);
const renderScene = SceneMap({
post: PostView,
trip: TripView,
});
const scrollY = useRef(new Animated.Value(0)).current;
const headerHeight = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT], // [0, 50]
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT], // [120,70]
extrapolate: 'clamp'
});
const profileImageHeight = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
outputRange: [PROFILE_IMAGE_MAX_HEIGHT, PROFILE_IMAGE_MIN_HEIGHT],
extrapolate: 'clamp'
});
const profileImageMarginTop = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
outputRange: [
HEADER_MAX_HEIGHT - PROFILE_IMAGE_MAX_HEIGHT / 2,
HEADER_MAX_HEIGHT + 5
],
extrapolate: 'clamp'
});
const headerZindex = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT, 120],
outputRange: [0, 0, 1000],
extrapolate: 'clamp'
});
const profile = useSelector(state => state.profile.userProfile );
return(
<SafeAreaView style={styles.container}>
<Animated.View style={[styles.backgroundImage, {
height: headerHeight,
zIndex: headerZindex,
elevation: headerZindex, //required for android
}]}>
</Animated.View>
<ScrollView
style={{flex: 1}}
scrollEventThrottle={16}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: scrollY}}}],
)}
bounces={true}
nestedScrollEnabled={true}
>
<View style={{overflow: 'hidden', position: 'absolute', top: HEADER_MIN_HEIGHT/2, left: HEADER_MIN_HEIGHT*2.6}}>
<TouchableOpacity><Icon color='white' name='camera-retro' size={30} /></TouchableOpacity>
</View>
<Animated.View style={[styles.profileImgContainer,{
height: profileImageHeight,
width: profileImageHeight,
marginTop: profileImageMarginTop,
borderRadius: PROFILE_IMAGE_MAX_HEIGHT/2,
}]}>
<Image source={{uri: profile.userAvatar}} style={styles.profileImg} />
</Animated.View>
[...]
<TabView
style={{
flex: 4.2,
}}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
renderTabBar={props => {
return(
<Animated.View style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
zIndex: headerZindex,
elevation: headerZindex,
// transform: [{ translateY: tabBarHeader }],
// marginTop: tabBarHeader
}}>
<TabBar {...props} />
</Animated.View>
)
}}
/>
</ScrollView>
</SafeAreaView>
);
};
const PostView = props => {
return(
<View>
<FlatList
style={{marginTop: 53}}
data={imgData}
numColumns={NUM_COLUMNS}
keyExtractor={item => item.id}
renderItem={(itemData) => (
<TouchableOpacity>
<Image source={itemData.item.source} style={{height:WIDTH/3, width:WIDTH/3, borderColor: 'white', borderWidth: 2}}/>
</TouchableOpacity>
)}
/>
</View>
)
}
const TripView = props => {
return(
<Button title="Trip Display" />
)
}
export default ProfileScreen;
const styles = StyleSheet.create({
//...
})
I also encountered the error: 'Virtualized list should never be nested inside plain ScrollView' and found out props like ListHeaderComponent
or use onStartShouldSetResponderCapture
.
But I can't figure out a proper progress in order to implement those, or to be precise, I don't know where to go next
Please help me. Thank you very much!