Hidding custom tabbar in nested stack navigation - react-native

I have a tabbar looking like the one below. inside every tab I have a nested stack navigator.
I want to hide the tabbar whenever the stack is not on its initial route. In the documentation, it is stated like one is supposed to place the stack in the initial stack navigator.
https://reactnavigation.org/docs/hiding-tabbar-in-screens/
However, this does not make sense and as I have my signup and all the routes where the customer is not logged in there I think it is counter-intuitive to place the entire application in one stack navigator. Further, it diminishes the purpose of having a tab-navigation in the first place.
I have also tried the tabBarVisible prop and change this conditionally. However, this provides me with some issues.
First, it does not seem to work on mu custom tabBar. Further, it gives me the state of every tab for every render. Thus it causes some unwanted behavior. lastly, it is not recommended from the documentation.
<Tab.Navigator
tabBar={props => (
<TabBar
onPress={tabIndex => changeTab(tabIndex, props)}
display={false}
values={[
{ title: "Balance", icon: 'view-dashboard'},
{ title: "Faktura", icon: 'file-pdf'},
{ title: "Rabatter", icon: 'gift'},
{ title: "Profil", icon: 'account-details'},
{ title: "Mere", icon: 'dots-horizontal' }
]}/>
)}>
<Tab.Screen
name="Balance"
component={BalanceStackNavigator}
/>
<Tab.Screen
name="Invoice"
component={InvoiceStackNavigator}
/>
<Tab.Screen
name="BlueBenefit"
component={BlueBenefitStackNavigator}
/>
<Tab.Screen
name="User"
component={UserStackNavigator}
/>
<Tab.Screen
name="CrossSale"
component={CrossSaleStackNavigator}
/>
</Tab.Navigator>
So basically my question is, how I achieve this behavior of hiding the tabs when not on the initial route in the best way?

I ended up using what was recommended by the documentation, which is to place all of my screens in my app stack instead of the individual nested stacks.

Related

React-navigation v6: Custom tab bar not navigating between screens on Android

I can't seem to figure out what is causing this code to not work on Android. It works well on iOS. It is a custom tab bar which lives inside a screen of the "normal" bottom tab bar. It has three screens and when switching between them I can see that things are happening under the hood, it's just that only the last tab screen is shown. All of the screens are being rendered and using useFocusEffect I can see that the screens are getting focused when the buttons are pressed, but they are not shown. So it's like they´re hidden behind the last tab screen.
The code is pretty much exactly taken from the example in the react-navigation guide (and I have also tried this with the exact example code):
https://reactnavigation.org/docs/custom-navigators
except that the top level <view>s had to switch places for the tab bar to be visible.
The tab navigator is then embedded like this (I have just replaced the actual names with numbers):
const 4Tab = createCustomTabNavigator<4ParamList>()
export default function 4TabNavigator() {
return (
<4Tab.Navigator initialRouteName="1">
<4Tab.Screen name="1" component={1Navigator} />
<4Tab.Screen name="2" component={2Navigator} />
<4Tab.Screen name="3" component={3Navigator} />
</4Tab.Navigator>
)
}
which in turn is embedded like so (4TabNavigator):
const Tab = createBottomTabNavigator() // Normal tab bar
const TabStack: React.FC<Props> = props => {
return (
<Tab.Navigator
initialRouteName={TabRouteName.1}
screenOptions={tabScreenOptions}>
<Tab.Screen name={TabRouteName.1} component={1Navigator} />
<Tab.Screen name={TabRouteName.2} component={2Navigator} />
<Tab.Screen name={TabRouteName.3} component={3Stack} />
<Tab.Screen
name={TabRouteName.4}
component={4TabNavigator}
options={{ lazy: true }}
/>
<Tab.Screen name={TabRouteName.5} component={5Navigator} />
</Tab.Navigator>
)
}
I'm not sure if this is an issue with react-navigation at all, but I also don't know what else could be wrong. I have looked through react-navigation's GitHub issues, and of course here on SO, but to no avail.

Navigating from a nested screen to another nested screen in a different Tab Stack (react-navigation)

I'm using react navigation 5 (with react native) and my navigation set up looks like this:
// Root
<NavigationContainer>
<RootStack.Navigator mode="modal" headerMode="none">
<RootStack.Screen name="Auth" component={AuthStackNavigator} />
<RootStack.Screen name="App" component={BottomTabsNavigator} />
</RootStack.Navigator>
</NavigationContainer>
// App
<Tab.Navigator tabBar={bottomTabBar}>
<Tab.Screen name="ScreenA1" component={StackA} />
<Tab.Screen name="ScreenB1" component={StackB} />
</Tab.Navigator>
// TAB A
<StackA.Navigator headerMode="none">
<StackA.Screen name="ScreenA1" component={ScreenA1} />
<StackA.Screen name="ScreenA2" component={ScreenA2} />
</StackA.Navigator>
// TAB B
<StackB.Navigator headerMode="none">
<StackB.Screen name="ScreenB1" component={ScreenB2} />
<StackB.Screen name="ScreenB2" component={ScreenB2} />
</StackB.Navigator>
So inside my App i have 2 Bottom Tabs (Tab A and Tab B) each having 2 nested screens (Screen A1>A2 and B1>B2).
So when i tap on Tab A i go to ScreenA1 then in there i can move on to ScreenA2. Same for Tab B.
Now on ScreenB2 from Tab B i have a button that should navigate the user to Screen A2 with some data to prefill on that screen.
If i do it like this:
navigation.navigate('ScreenA1', {
screen: 'ScreenA2',
params: { data },
});
I'll land on ScreenA2 with the data, but:
If i visited ScreenA2 before, the previous state persists and i can see its old state and data.
If i never visited ScreenA2 before it's now not inside the Tab A Stack, but instead pushed on top of ScreenB2.
So i guess i would need to reset the screen before i navigate to it (or unmount whenever i leave it) and also make sure that it gets put inside the Tab A Stack on creation, so when i try to call goBack from Screen A2 i land on Screen A1. Maybe navigate back to root first and call the screen from there without the user seeing all screens flashing up.
You can replace
navigation.navigate('ScreenA1'...
By
navigate.push('ScreenA1'...
That way, a new route will be added to your navigation stack, with new state and props.
Resolved it with this:
navigation.dispatch(
CommonActions.reset({
index: 0,
routes: [
{
name: 'ScreenA1',
state: {
routes: [
{
name: 'ScreenA1',
},
{
name: 'ScreenA2',
params: { data },
},
],
},
},
],
}),
);
Notice how i have 'ScreenA1' twice in there. I guess that's necessary because the Navigator and the first Screen inside the Navigator share the same route name

Reset StackNavigator from within the Component

BROAD PROBLEM (for individuals searching for the same solution):
I have an app with a switch-based StackNavigator ("MainStack"). I would like to reset MainStack as {index: 1, routes: [{name: 'Home', name: 'NestedStack'}]}, and I would like to call this from within the MainStack component (see below)
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
{
anon ?
<Stack.Screen name="Auth" component={AuthScreen} />
<Stack.Screen name="MainStack" component={MainStackScreen} />
}
</Stack.Navigator>
</NavigationContainer>
);
}
const MainStack = createStackNavigator();
function MainStackScreen({navigation, route}) {
*** An event occurs here ***
*** And I'd like to respond to the event with a navigation.reset() ***
return (
<MainStack.Navigator>
<MainStack.Screen name="Home" component={Home} />
<MainStack.Screen name="NestedStack" component={NestedStackScreen} />
</MainStack.Navigator>
);
}
I have tried several different combinations of navigation.dispatch({...CommonActions.reset({}), target}), producing different errors from the screen not existing to issues with screens.options.
I would also greatly appreciate advice on how to specify which screen to start with for NestedStack... would this just be {name: 'NestedStack', screen: 'NestedScreen'}??
SPECIFIC USE-CASE:
What I am really trying to achieve is Linking. I have users opening links when the app is closed (which I am accessing via Linking.getInitialURL) and from Camera screens within the app (which I am listening to via Linking.addEventListener). I have placed both of these in the MainStack component to intercept links as the app switches from the auth flow and as a QR code is scanned within the app.
I have to listen to changes in the MainStackScreen route, then set a variable isNavigatorMounted to true once the route is fully mounted, and then that triggers my check for Linking.getInitialURL.
Please address the Broad Question for general StackOverflow users, but if this is a terribly inefficient method, please let me know.
This answer was provided https://discord.com/channels/102860784329052160/288388742929055765/770038660220715028
You do not need CommonActions or anything. Simply apply the following to the StackNavigator's navigation object:
navigation.reset({
routes: [{ name: 'Home' }, { name: 'NestedStack' }],
});

How to disable swipe action in specific screen on react navigation 5?

im using react navigation 5 on my project, and somewhere in the project i need to make nested navigators like below:
*Stack Navigator
**Material top tab navigatior
***Material bottom tab navigator
**** Stack Navigator
My goal is, actually i want to have 3 static screens like instagram. On the left side will be Camera, then in the center actual content and in the right side something else. And only when you are in center i want to bottom tabs are visible thats why i created it like that. In this case in all the screens you can swipe right or left for reaching to camera or right component.
But what i want to do is, i want do "disable" swipe action in detail screens in center component. Becose center component which is material bottom tab navigator above, contains some stack navigators also, like posts, postDetail. and when i go to postDetail i want to disable swipe left or right action. Becouse in some details im using some swipeable elements like react native swiper etc.
I have tried to give gesturesEnabled: false , swipeEnabled: false in material bottom navigator as props but it doesn't work becouse it's a bottom tab navigator and doesn't take these params.
I also tried to catch state index and if its greater than 0 or 1 i would disable it but in material top tab navigator doesn't change the index when i go to postDetail for example. It's not working like previous version which is react navigation 4.
const BlogStack = createMaterialTopTabNavigator();
const BlogStackNavigator = ({ navigation, route }) => {
return (
<BlogStack.Navigator
initialRouteName="Blogs"
tabBarOptions={{
style: {
height: 0,
},
}}
//swipeEnabled={route.state && route.state.index > 0 ? false : true}
>
<BlogStack.Screen name="Camera" component={Camera} />
<BlogStack.Screen name="Blogs" component={Blog} />
<BlogStack.Screen name="Timeline" component={Profile} />
</BlogStack.Navigator>
);
};
Try setting gestureEnabled to false in Screen's options.
<Screen name={key} options={{gestureEnabled: false}} component={Component} />
You need to set gestureEnabled to false but it's not sent as a prop. You need to set it in the options prop. If you want to set it for all screens you can place it in the navigator like this:
<BlogStack.Navigator
initialRouteName="Blogs"
tabBarOptions={{
style: {
height: 0,
},
}}
screenOptions={{gestureEnabled: false}}
>
or for only specific screens:
<BlogStack.Screen name="Camera" options={{gestureEnabled: false}} component={Camera} />

How does one implement expanding card animation/transition using react navigation (react-native)?

What I want to achieve is an effect similar to react-native-card-modal
But I also want to change the route. The react native card modal uses the same component in which you have grow() and shrink() functions. The problem and the reason I want to avoid this way of implementing this is that I will have two levels of going inside the card and I will also have deep linking and I need to use a good navigation like react navigation for that.
I will also need to go back. Looking for an elegant solution but feel free to share any tutorials to related transitions. Thanks!
You can achieve this with a custom transitioner in react-navigation.
This blog post series has a great detailed explanation, with a few examples.
Edit: July 2022
The Transitioner has been removed in the latest react-navigation in favor of using Animated configurations directly in the navigator options under a property called cardStyleInterpolator.
Stolen shamelessly from the docs - to create a fade transition you would create an interpolator:
const forFade = ({ current }) => ({
cardStyle: {
opacity: current.progress,
},
});
And then use it in the navigator's properties:
<Stack.Screen
name="Profile"
component={Profile}
options={{ cardStyleInterpolator: forFade }}
/>
For more examples see the docs.