Tab Bar

The TabBar component gives you a native tabbed interface in iOS and Android. You create one TabBarItem child per tab and idenfity them using the title prop. On the 'contacts' scene in our email example, we separate the personal and work contacts by having a separate tab for each. We set the primary prop to false because these aren't top level navigation tabs.

import {TabBar, TabBarItem} from 'navigation-react-native';

var Contacts = () => (
  <TabBar primary={false}>
    <TabBarItem title="Personal">
      <ScrollView />
    </TabBarItem>
    <TabBarItem title="Work">
      <ScrollView />
    </TabBarItem>
  </TabBar>
);

Adding Top Level Navigation Tabs

Top level navigation tabs are different. On iOS, and sometimes Android, they stay at the bottom of the screen the whole time. Each tab has its own stack of scenes. You navigate forward and back within the tab. To recreate these you render a TabBar component at the root of your app and set its primary prop to true. Then you assign each TabBarItem a NavigationStack component with its own StateNavigator.

Let's add top level tabs to our email example. One tab for the user's inbox and another for their list of contacts. We create a new StateNavigator for the 'contacts' tab. It contains two States so the user can view all their contacts and edit the details of an individual contact.

var contactsNavigator = new StateNavigator([
  {key: 'contacts'},
  {key: 'contact', trackCrumbTrail: true}
]);

var {contacts, contact} = contactsNavigator.states;
contacts.renderScene = () => <Contacts />;
contact.renderScene = ({id}) => <Contact id={id} />;

contactsNavigator.navigate('contacts');

We change the App code from the Setup instructions to return a TabBar component. We give each tab its own NavigationStack and StateNavigator. For example, we give the 'contacts' tab the contactsNavigator we just created. To make them look like top level tabs, we assign each one an image as well as a title.

var App = () => (
  <TabBar primary={true}>
    <TabBarItem title="Inbox" image={require('./home.png')} badge={unreadCount}>
      <NavigationHandler stateNavigator={stateNavigator}>
        <NavigationStack />
      </NavigationHandler>
    </TabBarItem>
    <TabBarItem title="Contacts" image={require('./contacts.png')}>
      <NavigationHandler stateNavigator={contactsNavigator}>
        <NavigationStack />
      </NavigationHandler>
    </TabBarItem>
  </TabBar>
);

We assign the 'inbox' tab a badge that shows the number of unread emails. The badge won't display on Android unless we change to a MaterialComponents theme in the styles.xml.

<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar.Bridge">

Scrolling the Navigation Bar on Android

By wrapping the contacts scene in a CoordinatorLayout component we prevent the navigation bar from getting in the way on Android. The navigation bar gradually scrolls offscreen as the user moves down through their contacts. As soon as the user starts to scroll back up the navigation bar reappears. We render an empty TabBar inside the navigation bar to prevent the tabs scrolling off the screen, too. This functionality is only available on API level 21+ because we have to enable nested scrolling on the React Native ScrollViews.

<CoordinatorLayout>
  <NavigationBar title="Contacts">
    <TabBar primary={false} />
  </NavigationBar>
  <TabBar primary={false}>
    <TabBarItem title="Personal">
      <ScrollView nestedScrollEnabled={true} />
    </TabBarItem>
    <TabBarItem title="Work">
      <ScrollView nestedScrollEnabled={true} />
    </TabBarItem>
  </TabBar>
</CoordinatorLayout>