Reputation: 395
I want to create an app, that has both a fixed bottom and top tab navigation bar. See image:
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?
Upvotes: 0
Views: 2492
Reputation: 2185
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);
Upvotes: 1