JVG
JVG

Reputation: 21150

React Navigation - opening a modal from the tab bar

Using React Navigation (6), I've got bottom tabs set up as my main navigator:


export function TabNavigator() {
  const getColor = ({ focused, color }) => (focused ? palette.blue : color)

  return (
    <Tab.Navigator screenOptions={{ headerShown: false }}>
      <Tab.Screen
        name="home"
        component={HomeScreen}
        options={{
          tabBarLabel: "Home",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="home-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />
      <Tab.Screen
        name="favourites"
        component={FavouritesScreen}
        options={{
          tabBarLabel: "Favourites",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="heart-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />
      <Tab.Screen
        name="about"
        component={AboutYouScreen}
        options={{
          tabBarLabel: "About you",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="person-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />

       <Tab.Screen
        name="bottomchat"
        component={ChatNavigator}
        options={{
          tabBarLabel: "Check-in",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="chatbubble-ellipses-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />
    </Tab.Navigator>
  )
}

With the last tab (the ChatNavigator), I want the screen it opens to be a full screen modal, hiding the bottom tab bar (the user can exit out if it via a back button at the top).

Is this possible?

Upvotes: 11

Views: 19326

Answers (2)

itdevyan
itdevyan

Reputation: 29

Use listener/tabPress props with navigator, but navigator from TabNavigator component

export function TabNavigator( { navigation } ) { // <-- use this navitagor

      .........
      .........

      <Tab.Screen
        name="bottomchat"
        component={ AnyComponent } // <-- ignored
        options={{
          tabBarLabel: "Check-in",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="chatbubble-ellipses-outline" color={getColor(rest)} size={size} />
          ),
        }}
        listeners={() => ({
          tabPress: (e) => {
            e.preventDefault()
            navigation.navigate("ChatComponent") // <-- Here you put the name where the chat component is declared 
          },
        })}
      />
    </Tab.Navigator>
  )
}

Upvotes: 2

JVG
JVG

Reputation: 21150

As I was writing this question I found an answer which actually worked for me on this blog post

The solution:

  1. Create the full screen modal in your parent stack
  2. Pass in a mock component to the tab screen (this will never get called)
  3. Add a listener to the tab component that prevents default and then navigates to the page of your choice

So for me:

const ChatBase = () => <View style={{ flex: 1, backgroundColor: "red" }} />


export function TabNavigator() {
  const getColor = ({ focused, color }) => (focused ? palette.blue : color)

  return (
    <Tab.Navigator screenOptions={{ headerShown: false }}>
      <Tab.Screen
        name="home"
        component={HomeScreen}
        options={{
          tabBarLabel: "Home",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="home-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />
      <Tab.Screen
        name="favourites"
        component={FavouritesScreen}
        options={{
          tabBarLabel: "Favourites",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="heart-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />
      <Tab.Screen
        name="about"
        component={AboutYouScreen}
        options={{
          tabBarLabel: "About you",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="person-outline" color={getColor(rest)} size={size} />
          ),
        }}
      />

      <Tab.Screen
        name="bottomchat"
        /* Pass in a blank component as the base (this never gets shown) */
        component={ChatBase}
        options={{
          tabBarLabel: "Check-in",
          tabBarIcon: ({ size, ...rest }) => (
            <Ionicons name="chatbubble-ellipses-outline" color={getColor(rest)} size={size} />
          ),
        }}
        listeners={({ navigation }) => ({
          tabPress: (e) => {
            e.preventDefault()
            navigation.navigate("chat")
          },
        })}
      />
    </Tab.Navigator>
  )
}

In my root stack navigator, I've got a screen called chat that gets called:

export function MainNavigator() {
  return (
    <Stack.Navigator
      screenOptions={{
        cardStyle: { backgroundColor: "transparent" },
        headerShown: false,
      }}
    >
      <Stack.Screen name="main" component={TabNavigator} />
      <Stack.Screen name="chat" component={ChatScreen} />
    </Stack.Navigator>
  )
}

Upvotes: 28

Related Questions