Nested TabNavigator inside StackNavigator: controlling the header

You can use like this below, https://reactnavigation.org/docs/en/stack-navigator.html

//Screen1 Stack.

const Screen1 = createStackNavigator ({
    Home: {
        screen: Home,
        navigationOptions: {
            header: null //Need to set header as null.
        }
    }
});

//Screen2 Stack

const Screen2 = createStackNavigator ({
    Profile: {
        screen: Profile,
        navigationOptions: {
            header: null  //Need to set header as null.
        }
    }
});


let Tabs = createMaterialTopTabNavigator({
    Screen1:{
      screen: Screen1 //Calling Screen1 Stack.
    },
    Screen2:{
      screen: Screen2 //Calling Screen2 Stack.
    }
},{ tabBarPosition: 'bottom' }) //this will set the TabBar at Bottom of your screen.

let Stack = createStackNavigator({
    tabs:{
      screen: Tabs, //You can add the NavigationOption here with navigation as parameter using destructuring.
      navigationOptions: ({navigation})=>{
       //title: (navigation.state.routes[navigation.state.index])["routeName"]  
       //this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header. 

       //use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.

        //title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName

       //you can use switch case,on matching the route name you can set title of the header that you want and also header left and right icons similarly.

        switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
            case "Screen1":
                return {
                    title: "Home", 
                    headerLeft: (<Button
                        onPress={()=> alert("hi")}
                        title="Back"
                        color="#841584"
                        accessibilityLabel="Learn more about this purple button"
                    /> ),
                    headerRight: <Button title= "Right"/>
                }
            case "Screen2":
                return { 
                    title: "Profile",
                    headerLeft: (<Button
                        onPress={()=> alert("hi")}
                        title="Back"
                        color="#841584"
                        accessibilityLabel="Learn more about this purple button"
                    /> ),
                    headerRight: <Button title= "Right"/>
                }
            default:
                return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
        }
      }
    },
    otherScreen:{
      screen: OtherScreen
    }
})

//navigationOptions

  navigationOptions: ({navigation})=>{
   //title: (navigation.state.routes[navigation.state.index])["routeName"]  
   //this will fetch the routeName of Tabs in TabNavigation. If you set the routename of the TabNavigation as your Header. 

   //use the following title property,this will fetch the current stack's routeName which will be set as your header in the TabBar.

    //title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName

    switch ((navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName) {
        case "Screen1":
            return {
                title: "Home", 
                headerLeft: (<Button
                    onPress={()=> alert("hi")} //Here you can able to set the back behaviour.
                    title="Back"
                    color="#841584"
                    accessibilityLabel="Learn more about this purple button"
                /> ),
                headerRight: <Button title= "Right"/>
            }
        case "Screen2":
            return { 
                title: "Profile",
                headerLeft: (<Button
                    onPress={()=> alert("hi")} 
                    title="Back"
                    color="#841584"
                    accessibilityLabel="Learn more about this purple button"
                /> ),
                headerRight: <Button title= "Right"/>
            }
        default:
            return { title: (navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName }
    }
  }    

//alert(navigation.state)

{

    "routes":[
        {
            "key":"Screen1",
            "routeName":"Screen1",
            "routes":[
                {
                    "key":"Home",
                    "routeName":"Home",
                }
            ],
            "index":0,
            "isTransitioning":false,
            "key":"id-1530276062643-0"
        },
        {
            "key":"Screen2",
            "routeName":"Screen2",
            "routes":[
                {
                    "key":"Profile",
                    "routeName":"Profile",
                }
            ],
            "index":0,
            "isTransitioning":false,
            "key":"id-1530276062643-0"
        }
    ],
    "index":0,
    "isTransitioning":false,
    "routeName":"tabs",
    "key":"id-1530276062643-0"

}

//(navigation.state.routes[navigation.state.index])["routeName"] //(navigation.state.routes[navigation.state.index]["routes"])[(navigation.state.routes[navigation.state.index]["index"])].routeName

this will give the current route name of the tab inside StackNavigation.

The above code will set the title in root stack header where the TabBar resides as first route,so we are setting the header as null for the individual stack in TabBar. By using this way will provide Animation while switching the screens in TabBar since header will remain static.

you can find the working copy here https://www.dropbox.com/s/jca6ssn9zkzh9kn/Archive.zip?dl=0

Download this and Execute the following.

  1. npm install //to get dependencies

  2. react-native upgrade //to get android and ios folder

  3. react-native link //to link dependencies and libraries

  4. react-native run-ios (or) react-native run-android

    you can use the above, Let me know if any.


You can achieve the desired behavior with the current navigation stack configuration of yours. You might need to change couple of things and combine couple of properties but it is fairly simple when you get yor head around it.

I try to explain it with a small example.

Consider having below navigators;

const Tabs = createBottomTabNavigator({
    screen1: Tab1,
    screen2: Tab2
})

const Stack = createStackNavigator({
    tabs: {
      screen: TabsPage,
      navigationOptions: ({navigation}) => {
        return { title: (navigation.state.params && navigation.state.params.title ? navigation.state.params.title : 'No Title' ) }
      }
    },
    otherScreen: Page
})

As you can see I am setting the title parameter from the navigation state. To be ale to set the param for that navigator, we are going to get help from screenProps property;

class TabsPage extends Component {
  onTabsChange = (title) => {
    this.props.navigation.setParams({ title })
  }
  render() {
    return(<Tabs screenProps={{ onTabsChange: this.onTabsChange }} />)
  }
}

I created a wrapper component for tab navigator and passed down a function which is setting the title parameter.

For the last part we need to know how and when to use that function we passed down. For that we are going to use addListener navigation prop

class Tab1 extends React.Component {
  setTitle = () => {
    this.props.screenProps.onTabsChange('Title from Tab 1')
  }
  componentDidMount() {
    this.props.navigation.addListener('willFocus', this.setTitle)
  }
  render() {
    return <View><Text>{'Tab1'}</Text></View>
  }
}

When our tab focused the passed function will run and then set the title for that tab. You can use this process for setting different buttons or icons for the header. You can find the working snack here.