11.2 Nested Navigation

Building on what you have learned from the last module, it is time to look at how to manage the multi-level navigation requirements common to modern mobile applications. The initial setup is the same as for the Stack Navigator. Make sure that these dependencies are installed in your project.

yarn add @react-navigation/native
expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view

# Tab Navigation

The usage instructions for Tab.Navigator are very similar to the Stack.Navigator. Before you can use the tab navigator, you need to install the NPM module.

yarn add @react-navigation/bottom-tabs

Then import the createBottomNavigator function in your App.js module.



 

import React from 'react'
import { NavigationContainer } from '@react-navigation/native'
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'

Next create a new instance of the Tab object using the imported function. When invoked, it returns on object with two properties containing React components: Screen and Navigator.

const Tab = createBottomTabNavigator()

Then in the JSX, replace Stack.Navigator with Tab.Navigator and replace Stack.Screen with Tab.Screen.

export default function App() {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="About" component={AboutScreen} />
        <Tab.Screen name="Contact" component={ContactScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

# Add icons

It is easy to add icons to your tabs using the included react-native-vector-icons packages that Expo installs by default.

import Ionicons from 'react-native-vector-icons/Ionicons'

Then using the options prop on the Tab.Screen component, set the tabBarIcon. To make the icon dynamic, use a function that is given the focused state, color, and size params.




 
 
 
 
 


<Tab.Screen
  name="Contact"
  component={ContactScreen}
  options={{
    tabBarIcon: ({focused, size, color}) => (
      <Ionicons name={'ios-paper-plane'} size={size} color={color} />
    )
  }}
/>

You have complete flexibility in styling your tabs to match your designs. For example, let's use indigo as our key brand colour and then set up the tab bar options.

<Tab.Navigator
  tabBarOptions={{
    activeTintColor: 'hsl(275, 100%, 23%)', // indigo
    inactiveTintColor: 'hsl(275, 15%, 60%)',
    style: {backgroundColor: 'hsl(275, 100%, 93%)'}
  }}
>

Read the docs ...

For a full list of options, see the React Navigation API docs.

# Nested Navigators

It is pretty common to have a tab navigator as the primary means of navigation in a mobile app. And it is also quite common to have a nested stack navigator in a tab screen to allow the user to view more detailed content or perhaps to edit content.

So, how do you do that?

Because the React Navigation library uses factory functions to create unique instances of the various navigator objects, e.g. Stack or Tab, your application can deploy them in combination. Each will maintain its own independent navigation state.

Lets add a stack navigator to the home screen inside our tab navigator.

# Create a HomeDetailsScreen component

This will be added to the home screen stack navigator in a couple of steps from now.

function HomeDetailsScreen() {
  return (
    <SafeAreaView style={{flex: 1}}>
      <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
        <Text>Home Details!</Text>
      </View>
    </SafeAreaView>
  )
}

# Install the library

Next you will need to add the stack navigator library to your project.

yarn add @react-navigation/stack

And then create a new Stack instance ...

import { createStackNavigator } from '@react-navigation/stack'

const Stack = createStackNavigator()

# Create a new HomeStackScreen component

When the "Home" tab is selected in the Tab.Navigator you need to give it the Stack.Navigator to render, which will in turn render either the HomeScreen or the HomeDetailsScreen.

function HomeStackScreen() {
  return (
    <HomeStack.Navigator>
      <HomeStack.Screen name="Home" component={HomeScreen} />
      <HomeStack.Screen name="HomeDetails" component={HomeDetailsScreen} />
    </HomeStack.Navigator>
  )
}

Stack state is maintained

Notice that when you change tabs and then go back to a tab with a nested Stack.Navigator, the previous state of that stack is unchanged. i.e. it is still showing the same screen as before you switched tabs.

# Focus Aware StatusBar

When your app design calls for different background colours for the header on difference screens, the normal behaviour of the StatusBar component isn't helpful. It will keep the settings (light or dark) of the last component to render.

The solution is to create your own FocusAwareStatusBar, using the useIsFocused hook from React Navigation. This will cause your status bar to re-render when the screen is in focus, thereby updating the StatusBar with the correct settings.

import React from 'react'
import {StatusBar} from 'expo-status-bar'
import {useIsFocused} from '@react-navigation/native'

function FocusAwareStatusBar(props) {
  const isFocused = useIsFocused()
  return isFocused ? <StatusBar {...props} /> : null
}

export default FocusAwareStatusBar

You may now use this component as needed in your screen component JSX, with the same props as the regular Expo StatusBar.

# Drawer Navigation

Install the drawer module.

yarn add @react-navigation/drawer

Import and run the factory function.

import { createDrawerNavigator } from '@react-navigation/drawer'

const Drawer = createDrawerNavigator()

Extract the Tab navigation to its own TabScreen component and then update the top level navigation in App.js to look like this ...

export default function App() {
  return (
    <NavigationContainer>
      <Drawer.Navigator initialRouteName="Home">
        <Drawer.Screen name="Home" component={TabScreen} />
        <Drawer.Screen name="Setting" component={SettingsScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  )
}

# Resources

# GitHub repo

rlmckenney/mad9135-nested-nav-demo

# API docs

Last Updated: : 11/27/2020, 4:05:23 PM