React Navigation 6 (RN6) - Card stack within a modal - react-navigation-v6

I have a question about a card stack inside a modal stack as illustrated in the attached image.
So, just to repeat what I wanted to do. I have a screen with the option presentation: 'modal' that opens the green modal.
Inside that green modal, I have a button that should invoke a navigation call that should show the blue screen with option presentation: 'card' and the ability to go back to the green screen.
I have done something similar with the react-native-navigation library from WIX but I have no idea if that can be done with react-navigation.
Any help is much appreciated.
Cheers

I found the solution with Nesting navigators as described here
Basically, I created a ModalStack and used this stack in Screen component as shown below.
import React from 'react'
import { createNativeStackNavigator } from '#react-navigation/native-stack'
import { TransitionPresets } from '#react-navigation/stack'
import HomeView from '../screens/HomeView'
import ModalView from '../screens/ModalView'
import CardView from '../screens/CardView'
const RootStack = createNativeStackNavigator()
const ModalStack = createNativeStackNavigator()
const ModalStackView = () => (
<ModalStack.Navigator
screenOptions={{
headerShown: true,
}}>
<ModalStack.Screen
name="modalCard1"
component={ModalView}
options={{
presentation: 'modal'
}}
/>
<ModalStack.Screen
name="modalCard2"
component={CardView}
options={{
presentation: 'card'
}}
/>
</ModalStack.Navigator>
)
const Stacks = () => (
<RootStack.Navigator
screenOptions={{
headerShown: false,
}}>
<RootStack.Screen name="home" component={HomeView} />
<RootStack.Screen
name="modal"
component={ModalStackView}
options={{
presentation: 'modal'
}}
/>
</RootStack.Navigator>
)
export default Stacks
Here the Snack with the full code

Related

react native can not push a screen from 'screen modal'

i have a modal screen like below:
<Stack.Screen name="ForwardChatContent" component={ForwardChatContentScreen}
options={{
presentation: 'modal',
}} />
I want to push a screen from this screen, for example i have other screen like below:
<Stack.Screen name="ForwardChatToUser" component={ForwardChatToUserScreen}
But when using navigate, it's does not show new screen, can someone help? Thanks
Update, i changed ForwardChatContent and ForwardChatToUser into stack navigator like this:
const forwardStack = () => {
return <Stack.Navigator>
<>
<Stack.Screen name="ForwardChatContent" component={ForwardChatContentScreen}
options={{
presentation: 'modal',
}} />
<Stack.Screen name="ForwardChatToUser" component={ForwardChatToUserScreen}
options={{
// presentation: 'modal',
}} />
</>
</Stack.Navigator>
}
when navigate im using this code:
RootNavigation.navigate('ForwardChat', {message : props.currentMessage})
But in ForwardChatContent i got error ERROR TypeError: undefined is not an object (evaluating 'route.params.message')*
Because Im using this code to get message :
const message = route.params.message
Can u provide some way to get the params, thanks
It's because when you open a screen as Modal, it is treated as a separate set out of your existing Navigation Stack, it expects a Modal to be a NavigationStack, not just a Screen.
In your case, ForwardChatContentScreen is just a simple <Stack.Screen> it doesn't have a navigation stack.
Change it to NavigationStack from Screen it will work and open the NavigationStack as Modal having your screen as root, then it will work.
Check demo here
Cheers.

initial Route name not working in React native expo project

I am making an instagram clone. i have made it upto navigation. but in my expo app the screen which is added 1st after Stack.Navigator (in below case it is homescreen) shows up on th app, but if i change the initial route name to the second screen it has no effect and still the homescreen shows up. However if i add the 2nd screen before the 1st screen in the code below the 2nd screen shows up.
initial route name seems to have no effect.
import Homescreen from './screens/Homescreen';
import NewPostScreen from './screens/NewPostScreen';
import { createStackNavigator } from '#react-navigation/stack';
import { NavigationContainer } from '#react-navigation/native';
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRoutName="1" screenOptions={{ headerShown: false }}>
<Stack.Screen name="1" component={Homescreen} options={{ title: 'Home' }} />
<Stack.Screen name="2" component={NewPostScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
The prop name is wrong. The right name is initialRouteName. Please, rename it.

How to navigate from Home screen to Login screen in a nested navigation ? React Navigation v6

So I'm trying to improve my navigation in my React Native project using React Navigation. I would like to know how to navigate through a nested navigator from Home to Login screen.
navigation.ts
export type RootStackParamList = {
AuthorizedTabStack: BottomTabScreenProps<AuthorizedTabNavigationList>;
AuthorizedModalStack: NavigatorScreenParams<AuthorizedModalList>;
UnauthorizedStack: NavigatorScreenParams<UnauthorizedStackList>;
};
export type AuthorizedTabNavigationList = {
Home: undefined;
Planner: undefined;
};
export type AuthorizedModalList = {
InputModal: undefined;
};
export type UnauthorizedStackList = {
Login: undefined;
};
In my MainNavigator.tsx, I've implemented this...
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
{auth.currentUser ? (
<>
<Stack.Screen name="AuthorizedTabStack" component={TabNavigation} />
<Stack.Screen
name="AuthorizedModalStack"
component={ModalNavigation}
/>
</>
) : (
<Stack.Screen
name="UnauthorizedStack"
component={UnauthorizedStack}
/>
)}
</Stack.Navigator>
</NavigationContainer>
The UnauthorizedStackList is basically a StackNavigator
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Navigator>
I tried using this and there is an error: The action 'NAVIGATE' with payload {"name":"UnauthorizedStack","params":{"screen":"Login"}} was not handled by any navigator.
Do you have a screen named 'UnauthorizedStack'?
const navigation = useNavigation<NavigationProps>();
const handlePressLogOut = () => {
logOut()
navigation.navigate('UnauthorizedStack', { screen: 'Login' });
};
Please let me know if there are better practices on nested navigator or anything else in the code as well. I would like to learn more!
Your navigation.navigate(...) is called before auth state updates, because changing state is not happening right away. You cannot navigate to a screen, that is not rendered.
A few notes about your code:
you don't have to call navigation.navigate(...) after logging out, because you are conditionally rendering screens with auth.currentUser, so UnauthorizedStack will be rendered right after logOut(),
if Login screen is the only screen in your stack, probably there is no need for using stack,

Disable animation for a custom header in React Navigation

I would like to disable the screen animation for the header part of the Stack Navigator.
I have a common custom Header defined in the Stack Navigator via screenOptions.
And have default animations for screen transitions.
I want to make sure the animation happens only to the screen and not to my header component.
Since the header will a static content.
I've also tried making the headerMode as screen and float but that did not help.
I wanted to see if there is a property similar to animationEnabled but for the header component.
<Stack.Navigator
screenOptions= {{
headerMode: 'screen',
animation: 'fade',
header: (props) =>
<Header {...props} />
}}>
// Rest of my screens
</Stack.Navigator>
What you could do is completely separate the header from your Navigator, and use a ref to control navigation from it. Something like this:
const App = () => {
const navigationRef = useNavigationContainerRef()
return (
<View>
<Text>This header won't animate!</Text>
<Text onPress={() => navigationRef.navigate('Home')}>Link</Text>
</View>
<NavigationContainer ref={navigationRef}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Other" component={OtherScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}

how to display headers in react navigation with TabNavigation

I noticed that views in StackNavigation show the header title but if I set those same screens in a TabNavigation it doesn't show a header. It only shows a header if I wrap a StackNavigation either around each tab, or wrap the TabNavigation nested inside a StackNavigation.
Why don't screens in TabNavigation show a header - is that expected behavior? If so, is it better to have a StackNavigation in each tab, or one big StackNavigation around the TabNavigation?
// tabs navigation doesn't show a header title in each screen
const TabsNavigator = TabNavigator({
Home: {
screen:HomeScreen,
},
Profile: {
screen: ProfileScreen,
},
}, {
tabBarOptions: {
activeTintColor: '#e91e63',
},
navigationOptions: {
header: {
visible: true,
},
},
});
Header shows when I wrap it in a StackNavigator
default StackNavigator({
Home: { screen: TabsNavigator },
});
Or is it better to do it this way
export TabsNavigator = TabNavigator({
Home: {
screen:StackNavigator({
Home: { screen: HomeScreen },
}),
},
Profile: {
screen: StackNavigator({Profile: {screen: ProfileScreen}}),
},
}, {
tabBarOptions: {
activeTintColor: '#e91e63',
},
navigationOptions: {
header: {
visible: true,
},
},
});
Even if this is a fairly old question, I had myself this question a couple days ago, so I'll be adding my two cents about it hoping this will be useful for someone else in the future as well.
React Navigation is an amazing product with a high amount of customization, but that also turns out sometimes to be confusing to begin with, which also applies to some sections of the documentation. navigationOptions as of the current version states, is common for all screens but the "list of available navigation options depends on the navigator the screen is added to." https://reactnavigation.org/docs/tab-navigator.html#navigationoptions-used-by-tabnavigator hence the header option doesn't work because it's not available for TabNavigator itself.
Regarding your question on which approach is the best that depends on what do you want to accomplish with the navigation for your app itself. If you put your TabNavigator inside a StackNavigator the header component will be common for all of the tabs inside the TabNavigator, meaning the tab transition will take effect but the header won't move from its top position. If you on the other hand choose to nest a StackNavigator inside every tab, the header will render inside every tab, meaning the header will move along the tab transition animation.
I made a quick demo for you to see the difference, the code is also available if you wanna play with it.
I understand that this question already has an answer to it, but I would like to just show the way that I was able to make this work for myself using React Navigation > 5.x.x.
Juan's answer describes the best way to set this up, but also showing how it's done so that people can understand what the navigation looks like is also a bit of help for us visual learners. This was a little bit hard for me to understand at the time but hoping that it will help others in the future.
I find the best way to look at this is by breaking down the hierarchy of your apps routes. For me mine looked something like this.
LoginNavigation HomeNavigation
| |
LoginScreen RegisterScreen HomeStack ProfileStack
| |
HomesScreen ProfileScreen
My LoginNavigation stack contains two screens the Login screen and Register screen. In my app I don't need tab navigation set here so it's appropriate to just have these screens wrapped in a StackNavigator. On the other hand I have my HomeNavigation stack which contains a Home screen and a Profile screen. The only issue is that my screens are wrapped in a TabNavigator which does not produce a header.
To fix this we actually need to wrap our two screens (HomeScreen & ProfileScreen) in StackNavigators and then wrap everything with the TabNavigator. After realizing this, it actually makes much more sense to do it this way. Why? Well let's say that in my home screen I have some posts and I want the user to be able to see the post and all the comments that come with it. Well I wouldn't setup another Tab for this because that would just be silly. Instead we would add it to our HomeStack navigation instead like so.
HomeNavigation
|
HomeStack ProfileStack
| |
HomesScreen PostScreen ProfileScreen
This structure now seems apparent when looking at something like the ProfileStack where there could be multiple branches to this stack, such as Settings, Followers, Pictures...
Hopefully displaying the structure helps someone understand this as well as it helped me.
Below is my AppStackNavigator file to see the structure in code.
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import Login from '../screens/Login';
import Register from '../screens/Register';
import FirstProfileImage from '../screens/FirstProfileImage';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Home from '../screens/Home';
import Profile from './../screens/Profile';
const LoginStack = createStackNavigator();
const HomeTabs = createBottomTabNavigator();
const HomeStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const AppStack = createStackNavigator();
const LoginStackNavigator = () => (
<LoginStack.Navigator screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}}>
<LoginStack.Screen name="Login" component={Login}/>
<LoginStack.Screen name="Sign Up" component={Register}/>
<LoginStack.Screen name="Profile Image" component={FirstProfileImage}/>
</LoginStack.Navigator>
)
const HomeStackNavigator = () => (
<HomeStack.Navigator>
<HomeStack.Screen name="Home" component={Home} />
</HomeStack.Navigator>
)
const ProfileStackNavigator = () => (
<ProfileStack.Navigator>
<ProfileStack.Screen name="Profile" component={Profile} />
</ProfileStack.Navigator>
)
const HomeTabNavigator = () => (
<HomeTabs.Navigator>
<HomeTabs.Screen name="HomeStack" component={HomeStackNavigator} />
<HomeTabs.Screen name="ProfileStack" component={ProfileStackNavigator} />
</HomeTabs.Navigator>
)
const AppStackNavigator = () => (
<AppStack.Navigator initialRouteName={'HomeScreen'} screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}} headerMode='none'>
<AppStack.Screen name="LoginScreen" component={LoginStackNavigator}/>
<AppStack.Screen name="HomeScreen" component={HomeTabNavigator}/>
</AppStack.Navigator>
)
export default AppStackNavigator;
I do agree that this is a bit overkill and that we could eliminate the issue of having to keep track of each individual navigator, but it's what works for now until they add a feature for this. Hopefully this explanation helps a bit. Thanks for coming to my TED talk.
According to React-Navigation TabNavigator Docs there is no header navigationOption. Therefore, when you write the following code you are actually setting a nonexisting value thus what you are doing does not affect anything.
navigationOptions: {
header: { visible: true },
}
Sadly, you need a StackNavigator in this situation.
A general structure that I use is this, create a tab navigator and then render stacks for each tab. In this example I don't want a header on my LiveMap but I do on the other two tabs. The naming is up to you for whatever makes sense. Then inside each stack I render the screens that make sense.
const Tab = createBottomTabNavigator();
const TrainStack = createStackNavigator();
const MapStack = createStackNavigator();
const BusStack = createStackNavigator();
then each stack renders whatever screens I want for that subset
const MapNavigator = () => {
return (
<MapStack.Navigator
initialRouteName="LiveMap"
screenOptions={{headerShown: false}}>
<MapStack.Screen name="Live Map" component={LiveMap} />
<MapStack.Screen name="Show Routes" component={AllRoutes} />
<MapStack.Screen name="Select Routes" component={SelectRoutes} />
</MapStack.Navigator>
);
};
and the tab navigator will render my stacks for each tab.
const MainNavigator = () => (
<Tab.Navigator
tabBarOptions={{
activeTintColor: 'orange',
keyboardHidesTabBar: true,
}}
initialRouteName="Live Map">
<Tab.Screen
name="Buses"
component={BusNavigator}
options={{
tabBarLabel: 'Buses',
tabBarIcon: ({color, size}) => (
<BusIcon height={30} width={30} color={color} />
),
}}
/>
<Tab.Screen
name="Live Map"
component={MapNavigator}
options={{
tabBarLabel: 'Map',
tabBarIcon: ({color}) => (
<View
height={100}
width={100}
style={{
display: 'flex',
alignItems: 'center',
backgroundColor: '#fff',
paddingTop: 8,
borderRadius: 70,
}}>
<MapIcon height={50} width={50} color="orange" />
</View>
),
}}
/>
<Tab.Screen
name="Trains"
component={TrainNavigator}
options={{
tabBarLabel: 'Trains',
tabBarIcon: ({color, size}) => (
<TrainIcon height={30} width={30} color={color} />
),
}}
/>
</Tab.Navigator>