0
votes

I want to create an app, that has both a fixed bottom and top tab navigation bar. See image: enter image description here

After I finished the bottom navigation bar I tried the following in my App.js file:

return(
    <NavigationContainer>
        <Tab.Navigator>    //top navbar
            <Tab.Screen />
            ...
        </Tab.Navigator>
        <Tab.Navigator>    //bottom navbar
            <Tab.Screen />
            ...
        </Tab.Navigator>
    </NavigationContainer>
)

However, I get the error, that another navigator is already registered in this container and that I should not have multiple navigators under a single NavigationContainer. I found multiple guides about nesting tab and stack navigators, but how do I nest multiple tab navigators, that both update the central screen?

1
You will likely need to nest one of them. Is your plan for the top tabs to be visible only inside one of the bottom tabs? Or do you need them to both be visible at all times? Also, what kind of navigation actions would the top bar perform? - Marek Lisik
I want both of them to be visible all the time and update the central light-grey screen. What to you mean by navigation actions? They should both be tab navigators. :) - leabum

1 Answers

0
votes

AFAIK that is not possible without writing a custom navigator. Navigators need to be nested and need to have separate routes, so one tab navigator would need to be nested inside (as a tab of) the other.

Writing a custom navigator is something you definitely could consider. Here is a snack that modifies the example from react-navigation documentation:

https://snack.expo.io/@mlisik/thoughtful-stroopwafels

In the snack, the first two tabs are displayed on top, and remaining on the bottom. You would need to further modify them to match the appearance you are after with some custom options, perhaps reusing internal components from react-navigation.

It is by no means a complete solution, but should give you an idea of what is possible.

For completeness, I include the code here:

// App.js

import * as React from 'react';
import { Text, View, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNavigator } from './Navigator';

const Nav = createNavigator()

const Screen1 = () => <View style={{flex: 1, backgroundColor: 'red'}} />
const Screen2 = () => <View style={{flex: 1, backgroundColor: 'green'}} />
const Screen3 = () => <View style={{flex: 1, backgroundColor: 'yellow'}} />
const Screen4 = () => <View style={{flex: 1, backgroundColor: 'brown'}} />

export default function App() {
  return (
    <NavigationContainer>
      <Nav.Navigator>
        <Nav.Screen name="Tab 1" component={Screen1} />
        <Nav.Screen name="Tab 2" component={Screen2} />
        <Nav.Screen name="Tab 3" component={Screen3} />
        <Nav.Screen name="Tab 4" component={Screen4} />
      </Nav.Navigator>
    </NavigationContainer>
  )
}

// Navigator.js
// this is only slightly modified from https://reactnavigation.org/docs/custom-navigators#usenavigationbuilder

import * as React from 'react';
import { Text, TouchableOpacity, View } from 'react-native';
import {
  NavigationHelpersContext,
  useNavigationBuilder,
  createNavigatorFactory,
  TabRouter,
  TabActions,
} from '@react-navigation/native';

function TabButton({ route, descriptors, navigation, state }) {
  return (
    <TouchableOpacity
      key={route.key}
      onPress={() => {
        const event = navigation.emit({
          type: 'tabPress',
          target: route.key,
          canPreventDefault: true,
        });

        if (!event.defaultPrevented) {
          navigation.dispatch({
            ...TabActions.jumpTo(route.name),
            target: state.key,
          });
        }
      }}
      style={{ flex: 1 }}
    >
      <Text>{descriptors[route.key].options.title || route.name}</Text>
    </TouchableOpacity>
  )
}

function Navigator({
  initialRouteName,
  children,
  screenOptions,
  tabBarStyle,
  contentStyle,
}) {
  const { state, navigation, descriptors } = useNavigationBuilder(TabRouter, {
    children,
    screenOptions,
    initialRouteName,
  });

  const renderTab = (route) => (
    <TabButton
      route={route}
      descriptors={descriptors}
      state={state}
      navigation={navigation}
    />
  )

  return (
    <NavigationHelpersContext.Provider value={navigation}>
      <View style={[{ flexDirection: 'row' }, tabBarStyle]}>
        {state.routes.slice(0, 2).map(renderTab)}
      </View>
      <View style={[{ flex: 1 }, contentStyle]}>
        {descriptors[state.routes[state.index].key].render()}
      </View>
      <View style={[{ flexDirection: 'row' }, tabBarStyle]}>
        {state.routes.slice(2).map(renderTab)}
      </View>
    </NavigationHelpersContext.Provider>
  );
}

export const createNavigator = createNavigatorFactory(Navigator);