dev1ce
dev1ce

Reputation: 1717

Expo-Router Bottom tabs with nested Stack Screen

I am trying the new Expo-Router with a complex nesting navigation, currently i'm trying to display a screen with a presentation of modal, which itself is a part of a bottom tab navigation, but it is not presenting as a modal but as a simple stack screen:

Project layout:

app/
  _layout.tsx
  index.tsx
  (main)/
    _layout.tsx
    (tabs)/
      _layout.tsx
      home/
      create/
      profile/
      search/
      notifications/

(main)/(tabs)/_layout.tsx:

import {Tabs} from "expo-router/tabs";
export default function Layout() {
  return (
    <Tabs>
      <Tabs.Screen name='home'/>
      <Tabs.Screen name='search'/>
      <Tabs.Screen name='create'/> // should be presented as modal
      <Tabs.Screen name='notifications'/>
      <Tabs.Screen name='profile'/>
    </Tabs>
  )
}

(main)/(tabs)/create/_layout.tsx

import {Stack} from "expo-router/stack";
export default function Layout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{presentation: "modal"}}/>
    </Stack>
  )
}

The create page should be a stack with presentation set to modal in order for it to work/render as a modal screen right?

Reproducible repo:- https://github.com/theartificialguy/Threads

Upvotes: 5

Views: 12299

Answers (3)

chrfalch
chrfalch

Reputation: 147

I think the easiest solution is to change the href of the create tab-button to point to the url of the create page:

<Tabs.Screen name='create' options={{ href: "create" }} /> 

Upvotes: -2

Ulad Kasach
Ulad Kasach

Reputation: 12808

🎉 solution

You can do this systematically by using the navigation.getState() method

Here's an example of using it to hide the bottom bar anytime the active screen has nested navigation

    <Tabs
      screenOptions={({ route, navigation }) => {
        const state = navigation.getState();
        const hasNestedNavigation = state.routes[state.index].state?.index > 0; //  if the current state's route has a state, and its not the index of that route, then we've detected nested navigation
navigation
        return {
          tabBarStyle: {
            display: hasNestedNavigation ? 'none' : undefined, // hide for all nested navigation screens
          },
        };
      }}
    >
       {/** ... */}
    </Tabs>

This answer builds on the following resources:

You can extend this answer to customize when you do or do not want the bottom bar to display without limit given the navigation state, so any usecase you have should be covered.

⚠️ reminder

initialRouteName is required on each stack with expo-router

Otherwise, not only will this solution be broken on refreshes, but more importantly, for all situations with expo-router deeplinks and refreshes will not display the back button on your stack screens.

For example

export const unstable_settings = {
  initialRouteName: '/index',
};

ref: https://docs.expo.dev/router/advanced/router-settings/

Upvotes: 1

dev1ce
dev1ce

Reputation: 1717

I was able to solve it using the listener prop in Tabs.Screen.

Reference: https://stackoverflow.com/a/68900301/11685381

Upvotes: 2

Related Questions