Giter Site home page Giter Site logo

junghsuan / react-native-collapsible-tabview Goto Github PK

View Code? Open in Web Editor NEW
268.0 4.0 60.0 14.68 MB

This is only an implementation of tabview with collapsible header.

License: MIT License

JavaScript 68.23% Starlark 3.61% Java 14.49% Ruby 2.53% Objective-C 11.14%
react-native tabview collapsible-tab collapsible-header javascript collapsible

react-native-collapsible-tabview's Introduction

React native collapsible tabview example

This is just a simple example implementing a tabview with collapsible header. Here we use react-native-tab-view in our implemetation.

Dependencies

2022-07

  • Here I've upgraded react-native to 0.68.2 ,and react-native-tab-view to 3.1.1.
  • react-native-reanimated and react-native-gesture-handler have been removed by react-native-tab-view.
  • hermes is default enabled on both iOS and Android.
  • No changes to example code.
  • No furthur updates for old version.

2021-06

  • Be noticed that we are using react-native 0.62.2 in this example, some breaking changes may lead to crashes.
  • Accroding to this post, getNode() is deprecated. As a result, before 0.62.0 you should use ref.getNode() in order to get correct ref for the Animated component.

Usage

There are three examples under src folder:

  1. CollapsibleTabView: provides the most basic workable example without scrollable header and pull-to-refresh.
  2. HeaderScrollableTabView: provides scrollable header without pull-to-refresh.
  3. PullRefreshTabView: provides both scrollable header and pull-to-refresh.

It is critical to give the correct paddingTop and minHeight of contentContainerStyle of the FlatList

  contentContainerStyle={{
      ...
      paddingTop: HeaderHeight + TabBarHeight,
      minHeight: windowHeight - SafeStatusBar + HeaderHeight,
  }}

Demo

iOS ios Android Android

react-native-collapsible-tabview's People

Contributors

dependabot[bot] avatar junghsuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

react-native-collapsible-tabview's Issues

Lazy load

Thank you for the amazing job implementing and sharing this code with us as a base for multiple use cases.
Have you tried to implement lazy loading? I have been trying to implement it and facing some issues related to the scrollOffSet of the lazy loaded tabs, I'm facing the issue that you explain on the medium post where you introduce the syncScrollOffset to cover all the tabs scroll and keep them in sync, this won't work for lazy loading because the listRefArr does not hold the references for the tabs that are not yet loaded.
I'm trying to come up with a solution, but since you worked with the base idea maybe you know some "workaround" for this situation? Meantime if I find a way of handling it I will post here for further reference in case someone needs it.

SafeAreaView on iOS scrolls with the tabs

I have no issue with this on android, it works perfectly. But on iOS, when I scroll up, the status bar becomes transparent and I can see the header and other components being scrolled behind the status bar. I did wrap everything inside a SafeAreaView with a background color, but when I scroll, the background color of the SafeAreaView also scrolls up. The weird thing is, when I add a navigation header, the tab contents scroll behind it so that works well. Any idea on how I can fix this?

How to implement in class component

Any tutorial how to implement it in class component? I'm already write lot of my codes with class component and its kinda hard to implement this while this code written in hooks

Headers not sticking to top with React native 0.63.2

my react-native version is 0.63.2

"react-native-tab-view": "^2.14.4",
"react-native": "0.63.2",

I just copy-pasted the code from the Repo and the headers are not sticking to the top anymore
the screenshot is of Pixel_3a_XL_API_27
Screenshot 2020-09-28 at 10 36 02 PM

Q: Scroll stops automatically from header

Why does the scroll stops if I scroll from the Header? It stops if it reached the tabbar, so I'm not able to scroll to the bottom from the Header. How to solve this?

How to keep navigation header on top?

I have navigation header for showing back button and title. I just want to keep this on top and not get replaced by collapsed header or the tabs.

Any way to Implement with SectionList?

SectionList does not seem to have 'scrollToLocation' function. But Is the only List type I need to use for my tabs, is there anything I can do. Have currently tried everything.

Also what about ScrollView?

Can't click any view after scroll on release build.

It worked really well until I build the release version. I don't understand why everything stays clickable until I scroll down. I can't click any view on the screen. Even the back button on my Android phone cannot return to the previous screen. I don't know how to handle this. Please help me.

Breaks with safeareaview

Hi,

I tried the PullRefreshTabView example, but I'd like header and the scroll to respect the safe area view. By wrapping the screen around the SafeAreaView component, the screen breaks.

Any suggestion?

Thanks

item.value.scrollToOffset is undefined

Hi,

I was experimenting with the demo and came across this error => item.value.scrollToOffset' is undefined. Under what circumstances would this appear?

item.value.scrollToOffset not working for me

ScrollOffset does not sync fo each route, below is my code

import React, {useState, useEffect, useRef} from 'react';
import {
  SafeAreaView,
  StyleSheet,
  View,
  Text,
  Dimensions,
  Animated,
  Image,
  StatusBar
} from 'react-native';
import {TabView, TabBar, SceneMap} from 'react-native-tab-view';

import Icon from 'react-native-vector-icons/FontAwesome';
import IconF from 'react-native-vector-icons/Feather';
import ProfileFlatList from '../components/ProfileFlatList.js';
import { Thumbnail, Button, Tab, Tabs, TabHeading } from 'native-base';


const DATA = [
    {image:require('../../assets/4.jpg'),isDraft:true,draftCount:'24',draftIcon:'inbox'},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false}
];

const DATA1 = [
    {image:require('../../assets/4.jpg'),isDraft:true,draftCount:'24',draftIcon:'inbox'},
    {image:require('../../assets/3.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/1.png'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false},
    {image:require('../../assets/2.jpg'),view:'205k',heart:'110k', heartIcon:'heart',viewIcon:'play',iconColor:'rgba(255,255,255,0.95)',textColor:'rgba(255,255,255,0.95)',isDraft:false}
];
const TabBarHeight = 48;
const HeaderHeight = 390;
const tab1ItemSize = (Dimensions.get('window').width - 30) / 3;

class TabScene extends React.Component {
  render = () => {
    const windowHeight = Dimensions.get('window').height;
    const {
      numCols,
      data,
      renderItem,
      onGetRef,
      scrollY,
      onScrollEndDrag,
      onMomentumScrollEnd,
      onMomentumScrollBegin,
    } = this.props;
    return (
      <Animated.FlatList
        scrollToOverflowEnabled={true}
        numColumns={numCols}
        ref={onGetRef}
        scrollEventThrottle={16}
        onScroll={Animated.event(
          [{nativeEvent: {contentOffset: {y: scrollY}}}],
          {useNativeDriver: true},
        )}
        onMomentumScrollBegin={onMomentumScrollBegin}
        onScrollEndDrag={onScrollEndDrag}
        onMomentumScrollEnd={onMomentumScrollEnd}
        ItemSeparatorComponent={() => <View style={{height: 10}} />}
        ListHeaderComponent={() => <View style={{height: 10}} />}
        contentContainerStyle={{
          paddingTop: HeaderHeight + TabBarHeight,
          paddingHorizontal: 10,
          minHeight: windowHeight - TabBarHeight,
        }}
        showsHorizontalScrollIndicator={false}
        data={data}
        renderItem={renderItem}
        showsVerticalScrollIndicator={false}
        keyExtractor={(item, index) => index.toString()}
      />
    );
  };
}

const Profile = () => {
  const [tabIndex, setIndex] = useState(0);
  const [routes] = useState([
    {key: 'tab1', title: 'Tab1'},
    {key: 'tab2', title: 'Tab2'},
  ]);
  const scrollY = useRef(new Animated.Value(0)).current;
  let listRefArr = useRef([]);
  let listOffset = useRef({});
  let isListGliding = useRef(false);

  useEffect(() => {
    scrollY.addListener(({value}) => {
      const curRoute = routes[tabIndex].key;
      listOffset.current[curRoute] = value;
    });
    return () => {
      scrollY.removeAllListeners();
    };
  }, [routes, tabIndex]);

  const syncScrollOffset = () => {
    const curRouteKey = routes[tabIndex].key;
    listRefArr.current.forEach((item) => {
      // sync value except current route
      if (item.key !== curRouteKey) {
        // if header has not yet been collapsed, scroll to current offset
        if (scrollY._value < HeaderHeight && scrollY._value >= 0) {
          if (item.value) {
            item.value.scrollToOffset({
              offset: scrollY._value,
              animated: false,
            });
             // we should also update the offset here
            listOffset.current[item.key] = scrollY._value;
          }
       // if header has been collapsed, scroll to HeaderHeight
      } else if (scrollY._value >= HeaderHeight) {
          if (listOffset.current[item.key] < HeaderHeight ||
            listOffset.current[item.key] == null) {
            if (item.value) {
                item.value.scrollToOffset({
                  offset: HeaderHeight,
                  animated: false,
                });
                listOffset.current[item.key] = HeaderHeight;
              }
            }
        }
      }
    });
  };

  const onMomentumScrollBegin = () => {
    isListGliding.current = true;
  };

  const onMomentumScrollEnd = () => {
    isListGliding.current = false;
    syncScrollOffset();
  };

  const onScrollEndDrag = () => {
    syncScrollOffset();
  };

  const renderHeader = () => {
    const y = scrollY.interpolate({
      inputRange: [0, HeaderHeight],
      outputRange: [0, -HeaderHeight],
      extrapolateRight: 'clamp',
    });
    return (
      <Animated.View style={[styles.header, {transform: [{translateY: y}]}]}>
        <View style={{width: "100%",}}>
            <View style={{height:250,backgroundColor:'red'}}>
                <Image source={require('../../assets/2.jpg')} style={styles.topBG}></Image>
                <View style={styles.overlay} ></View>
            </View>
            <View style={{height:220, borderTopLeftRadius:30,borderTopRightRadius:30, marginTop:-40,backgroundColor:'#fff'}}>
                <View style={{flex:0.3,flexDirection:'row',}}>
                    <View style={{marginTop:-40,marginLeft:50,
                        marginBottom:15,
                        shadowColor: '#000',
                        borderRadius:600,
                        shadowOffset: {
                            width: 0,
                            height: 1,
                        },
                        shadowOpacity: 0.2,
                        shadowRadius: 1.50,
                        elevation:10}}>
                        <Thumbnail large source={require('../../assets/4.jpg')} style={{borderWidth:3,borderColor:'white',}}/>
                    </View>
                    <View style={{marginLeft:15, marginTop:10}}>
                        <Text>@itsandysworld</Text>
                        <Text style={{color:'#c6c6c6'}}>ID: MT1989861</Text>
                    </View>
                </View>
                <View style={{flex:1,justifyContent:'space-around'}}>
                    <View style={{flexDirection:'row',justifyContent:'space-evenly',paddingHorizontal:40,marginTop:20}}>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>504</Text>
                            <Text style={{color:'#898b90'}}>Following</Text>
                        </View>
                        <View style={{borderRightColor:'#f1f1f1',borderRightWidth:2,height:20,marginTop:10}}></View>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>210.8K</Text>
                            <Text style={{color:'#898b90'}}>Fans</Text>
                        </View>
                        <View style={{borderRightColor:'#f1f1f1',borderRightWidth:2,height:20,marginTop:10}}></View>
                        <View style={{marginHorizontal:15,alignItems:'center',}}>
                            <Text>2.8m</Text>
                            <Text style={{color:'#898b90'}}>Hearts</Text>
                        </View>
                    </View>
                    <View style={{flexDirection:'row',justifyContent:'space-evenly',marginTop:5,paddingHorizontal:40, marginTop:40}}>
                        <Button bordered style={{width:130},styles.outlineBtn}>
                            <Text style={styles.outlineText}>Message</Text>
                        </Button>
                        <Button bordered style={styles.outlineBtn}>
                            <IconF name="user-check" color="#000"></IconF>
                        </Button>
                        <Button bordered style={styles.outlineBtn}>
                            <IconF name="video" color="#000"></IconF>
                        </Button>
                    </View>
                    <View style={{padding:10,alignItems:'center', marginTop:30, marginBottom:5}}>
                        <Text style={{color:'#969498'}}>#AndyFam</Text>
                        <Text style={{color:'#969498'}}>What's up AndyFam <Icon name='heart' color='#969498'></Icon> Don't forget to</Text>
                        <Text style={{color:'#969498'}}>subscribe to my YouTube</Text>
                    </View>
                    <View style={{borderTopColor:'#efefef',borderTopWidth:1,width:'90%',marginTop:25, marginLeft:20,}}></View>
                </View>
            </View>
        </View>
      </Animated.View>
    );
  };

const rednerTab1Item = ({item, index}) => {
    return (
        <View style={{ flex: 1,
            marginLeft: index % 3 === 0 ? 0 : 10,
            width: tab1ItemSize,
            height: tab1ItemSize,
        }}>
            <Image style={{justifyContent: 'center',
                    alignItems: 'center',
                    width: '100%',
                    height: '100%',
                    borderRadius:8,
                    resizeMode: 'cover',}}
                source={item.image } />
            {item.isDraft &&
                <View style={{
                    ...StyleSheet.absoluteFillObject,
                    backgroundColor: 'rgba(0,0,0,0.6)',
                    borderRadius:8,
                    justifyContent: 'center',
                    alignItems: 'center',
                }} >
                    <IconF name={item.draftIcon} color='#fff' size={20} style={{marginTop:35}}></IconF>
                    <Text style={{color:'#fff',marginTop:5,fontSize:12}}>Draft</Text>
                    <Text style={{color:'#fff',fontSize:12}}>{item.draftCount}</Text>
                </View>
            }
            <View style={styles.bottomIcon}>
                <View style={styles.bottomIconRow} >
                    <Text style={{color:item.textColor,fontSize:12}}><Icon name={item.viewIcon} color={item.iconColor} size={8}></Icon> {item.view}</Text>
                    <Text style={{color:item.textColor,fontSize:12}}><Icon name={item.heartIcon} color={item.iconColor} size={8}></Icon> {item.heart}</Text>
                </View>
            </View>
        </View>
    );
  };

  const renderLabel = ({route, focused}) => {
    return (
        <>
            {route.key == 'tab1' &&
                <Image height={20} width={20} style={{tintColor:focused ?"#fd0d3a" :"#c3c3c3"}} source={require('../../assets/profile_list_icon.png')}/>
            }
            {route.key == 'tab2' &&
                <IconF size={20} name="heart" color={focused ?"#fd0d3a" :"#c3c3c3"} />
            }
        </>
    );
  };

  const renderScene = ({route}) => {
    const focused = route.key === routes[tabIndex].key;
    let numCols;
    let data;
    let renderItem;
    switch (route.key) {
      case 'tab1':
        numCols = 3;
        data = DATA;
        renderItem = rednerTab1Item;
        break;
      case 'tab2':
        numCols = 3;
        data = DATA1;
        renderItem = rednerTab1Item;
        break;
      default:
        return null;
    }
    return (
      <TabScene
        numCols={numCols}
        data={data}
        renderItem={renderItem}
        scrollY={scrollY}
        onMomentumScrollBegin={onMomentumScrollBegin}
        onScrollEndDrag={onScrollEndDrag}
        onMomentumScrollEnd={onMomentumScrollEnd}
        onGetRef={(ref) => {
          if (ref) {
            const found = listRefArr.current.find((e) => e.key === route.key);
            if (!found) {
              listRefArr.current.push({
                key: route.key,
                value: ref,
              });
            }
          }
        }}
      />
    );
  };

  const renderTabBar = (props) => {
    const y = scrollY.interpolate({
      inputRange: [0, HeaderHeight],
      outputRange: [HeaderHeight, 0],
      extrapolateRight: 'clamp',
    });
    return (
      <Animated.View
        style={{
          top: 0,
          zIndex: 1,
          position: 'absolute',
          transform: [{translateY: y}],
          width: '100%',
        }}>
        <TabBar
          {...props}
          onTabPress={({route, preventDefault}) => {
            if (isListGliding.current) {
              preventDefault();
            }
          }}
          style={styles.tab}
          renderLabel={renderLabel}
          indicatorStyle={styles.indicator}
        />
      </Animated.View>
    );
  };

  const renderTabView = () => {
    return (
      <TabView
        onIndexChange={(index) => setIndex(index)}
        navigationState={{index: tabIndex, routes}}
        renderScene={renderScene}
        renderTabBar={renderTabBar}
        initialLayout={{
          height: 0,
          width: Dimensions.get('window').width,
        }}
      />
    );
  };

  return (
    <>
    <StatusBar
        backgroundColor = "#fff"
        barStyle = "light-content"
    />
    <SafeAreaView style={{flex: 1, backgroundColor:'#fff'}}>
      <View style={{flex: 1}}>
        {renderTabView()}
        {renderHeader()}
      </View>
    </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
    header: {
        top: 0,
        height: 300,
        width: '100%',
        backgroundColor: '#40C4FF',
        alignItems: 'center',
        justifyContent: 'center',
        position: 'absolute',
    },
    tab: {elevation: 0, shadowOpacity: 0, backgroundColor: '#fff'},
    indicator: {backgroundColor: '#fd0d3a'},
    container: {
        flex: 1,
        flexDirection: "column",
        backgroundColor:'#fff'
    },
    row:{
        flexDirection:"column",
        margin:30,
    },
    overlay: {
        ...StyleSheet.absoluteFillObject,
        backgroundColor: 'rgba(0,0,0,0.4)',
    },
    topBG:{
        // flex: 1,
        width:'100%',
        resizeMode: 'cover',
    },
    outlineBtn:{
        borderColor:'#c3c3c3',
        paddingHorizontal:15,
        borderRadius: 3,
        alignItems:'center',
        height:45,
        // width:130
    },
    outlineText:{
        color:'#000',
        fontSize: 15,
        textAlign: 'center',
        padding:10,
        alignSelf:'center',
        backgroundColor: 'transparent'
    },
    tabBarUnderlineStyle:{
        backgroundColor:'#fff',
        height:2.5,
    },
    imageThumbnail: {
        justifyContent: 'center',
        alignItems: 'center',
        height: 100,
    },
    bottomIconRow:{
        flex:1,
        flexDirection:'row',
        justifyContent: 'space-evenly',
        paddingHorizontal:0,
    },
    bottomIcon:{
        //backgroundColor:'green',
        position:'absolute',
        bottom:0,
        width:'100%',
        marginBottom:8
    }
});

export default Profile;

any way to get this working with the default flatlist pull to refresh?

hello, thanks for this a great repo!

wondering if there's a way to get it working like this one on coinbase

RPReplay_Final1711043985.mov

coinbase actually opensourced their original implementation here, which seems very similar to yours except for a couple of things
https://github.com/coinbase/CBTabViewExample
https://www.coinbase.com/blog/coinbases-animated-tabbar-in-react-native?source=linkShare-3089be4219ff-1621699865

seems like recently they've updated it to have scroll on header as well as the native flatlist pull to refresh. could that be incorporated here?

Cannot pull to refresh

Thanks for your solution but I cannot pull to refresh when scroll down the list inside tab.

Do you have any to solution for this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.