I have a drawer navigator with many child navigators.
On Android, both Header back arrow and navigation bar back button work and go back to previous screens.
E.g., if I navigate to a bScreen1 through the drawer, both buttons get me back to the Home Screen of the App i.e. aScreen1
On IOS, Header back arrow work fine, HOWEVER, swipe back gets me back to my login screen inside the AuthNavigator instead of aScreen1, which has 0 sens to me because it is outside the drawerNavigator, and at the same level inside the RootNavigator. The swipe back does not perform a navigation.goBack().
I have already tried overriding the swipe behavior by adding a listener on 'beforeRemove' event and then calling navigation.goBack(), but the wrong screen still shows up for a instant before moving to the right one (previous one)
...
import { createDrawerNavigator } from '#react-navigation/drawer'
import { createStackNavigator } from '#react-navigation/stack';
const DrawerNavigator = createDrawerNavigator();
const rootStack = createStackNavigator();
...
drawerNavigator() {
return (
<DrawerNavigator.Navigator>
<DrawerNavigator.Screen component={aNavigator} />
<DrawerNavigator.Screen component={bNavigator} />
...
<DrawerNavigator.Screen component={zNavigator} />
</DrawerNavigator.Navigator>
)}
aNavigator() {
return (
<ANavigator.Navigator>
<ANavigator.Screen component={aScreen1} />
...
<ANavigator.Screen component={aScreen10} />
</ANavigator.Navigator>
)}
bNavigator() {
return (
<ANavigator.Navigator>
<ANavigator.Screen component={bScreen1} />
...
<ANavigator.Screen component={bScreen10} />
</ANavigator.Navigator>
)}
<rootStack.Navigator initialRouteName="AuthNavigator" screenOptions={{ headerShown: false }}>
<rootStack.Screen name="AuthNavigator" component={createAuthNavigator} />
<rootStack.Screen name="DrawerNavigator" component={drawerNavigator} />
</rootStack.Navigator>
Ps: I am using all the latest versions of navigation packages
I had the same issue when I set animationEnabled: false, if you set it to true you should be able to swipe to switch screens.
Related
I have a project in react native in which I wanted to implement a feature.
I have three screens
slash screen
login screen
home screen
the slash screens last 1 second and leave on the home screen while the number of times the app has been opened does not exceed 3 times.
otherwise we display the login screen to ask for a password
About how to make the splash screen last 1 second, on Splash component, put an useEffect like this:
function Splash({ navigation }) {
useEffect(() => setTimeout(navigation.navigate('Home')),[]);
return (
<View>
<Image />
</View>
);
}
About conditional first screen when open the app, you can pass initialRouteName as a prop of Stack.Navigator:
import { createStackNavigator } from '#react-navigation/stack';
const Stack = createStackNavigator();
function App() {
return (
<Stack.Navigator initialRouteName={times < 3 ? Slash : Login}>
<Stack.Screen name="Slash" component={Slash} />
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
);
}
Note: my examples above use StackNavigator and on React Navigation v6, it works the same as TabNavigator and other versions of React Navigation.
I'm using a stack navigator nested in a drawer navigator for a react native app using react navigation 6. I'm only showing the header for the stack navigator. I want to put a hamburger menu button on the far left of the header, BUT I also want the default stack navigation back button to be present.
I found the setting headerBackVisible in the documentation, but haven't been able to find any examples of anyone using it, so I'm not sure if I'm using it properly. I'm trying to pass it as headerBackVisible: true in my screen options. No matter what I do, if I put anything else in headerLeft, I cannot get the header back button to show.
Anyone have any suggestions or examples on how to put something to the left of the header back button?
Yes, headerBackVisible is not working with headerLeft. As a workaround, you can import default back button (HeaderBackButton) from #react-navigation/stack and return it along with your hamburger component. Something like this:
import { createStackNavigator, HeaderBackButton } from '#react-navigation/stack';
<Stack.Navigator
screenOptions={({ navigation, route }) => ({
headerLeft: (props) => {
return (
<>
<Text>Menu</Text> // Replace with your hamburger component
{props.canGoBack && <HeaderBackButton {...props} />} // THIS IS WHAT YOU NEED
</>
);
},
})}> ....
<Stack.Screen ... />
<Stack.Screen ... />
</Stack.Navigator>
Snack link: https://snack.expo.dev/#rabiarashid/react-navigation---stack-navigator-example
Update (for react-navigation v6):
In v6, HeaderBackButton is moved to elements library i.e.
import { HeaderBackButton } from '#react-navigation/elements';
Ref: https://reactnavigation.org/docs/upgrading-from-5.x/#some-exports-are-now-moved-to-the-element-library
In my react-native project I am using libraries
"#react-navigation/native": "^5.8.10",
"#react-navigation/stack": "^5.12.8",
I have nested navigator, like this:
// root level I have a stack navigator where it contains two screens, `Home` and `Settings`.
const App = ()=> {
const rootStack = createStackNavigator();
return (
<NavigationContainer>
<rootStack.Navigator>
<rootStack.Screen name="Home" component={Home} />
<rootStack.Screen name="Settings" component={Settings} />
</rootStack.Navigator>
</NavigationContainer>
);
}
// The Settings screen is a nested stack navigator
const Settings = ()=> {
const settingsStack = createStackNavigator();
return (
<settingsStack.Navigator>
<settingsStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<settingsStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</settingsStack.Navigator>
);
}
As you can see, the Settings screen is actually another level (nested) stack navigator.
On SettingsOneScreen, there is a button navigates user to SettingsTwoScreen.
const SettingsOneScreen = ({navigation}) => {
...
return (
...
<Button onPress={()=>navigation.navigate("SettingsTwo")}/>
)
}
Now, on SettingsTwoScreen, I have a button, I would like to close the whole settings navigator stack when user tap on the button. That's dismiss the whole settings stack & show user the Home. How to achieve it?
const SettingsTwoScreen = ({navigation}) => {
...
return (
...
<Button onPress={/*dismiss the settings stack*/}/>
)
}
(Of course I can't use the navigation.goBack() which only navigate user back to the previous screen i.e. SettingOneScreen in this case.)
1-) use navigate.
//this will go back to Home and remove any screens after that.
navigation.navigate('Home')
docs say that.
In a stack navigator, calling navigate with a screen name will result in different behavior based on if the screen is already present or not. If the screen is already present in the stack's history, it'll go back to that screen and remove any screens after that. If the screen is not present, it'll push a new screen.
2-) use reset.
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
})}
see docs about reset
3-) use replace then goBack.
//from SettingsOne call replace instead navigate
//this will remove SettingsOne and push SettingsTwo
navigation.replace('SettingsTwo');
//then from SettingsTwo
//calling goBack will back to home because SettingsOne removed in last step
navigation.goBack();
4-) use one stack with pop.
import { StackActions } from '#react-navigation/native';
//merge two stacks in one
<NavigationContainer>
<rootStack.Navigator>
<rootStack.Screen name="Home" component={Home} />
<rootStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<rootStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</rootStack.Navigator>
</NavigationContainer>
//back 2 screen
navigation.dispatch(StackActions.pop(2));
see docs about pop
for methods 1, 2 you can try snack here.
I have faced the same problem before, this will help you more
<Button onPress={()=>navigation.navigate("Settings",{
screen: 'SettingsTwo',
params: { data: data }//put here the data that you want to send to SettingTow
)}
/>
//more explanation
goto = (data) => {
navigation.navigate('parent_stack', {
screen: 'screen_on_children_stack',
params: { data: data }
});
}
You can create a Switch navigator for the "root" app and create two stacks "home" and "setting".
const Root = ()=> (createAppContainer(createSwitchNavigator(
{
Home: Home,
Settings: Settings,
},
{
initialRouteName: 'Home',
}
)
// The Settings screen is a nested stack navigator
const Settings = ()=> {
const settingsStack = createStackNavigator();
return (
<settingsStack.Navigator>
<settingsStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<settingsStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</settingsStack.Navigator>
);
}
Then you can easily switch between stacks of Home and Settings
this.props.navigation.navigate('Settings');
https://reactnavigation.org/docs/upgrading-from-4.x/#dismiss
navigation.dangerouslyGetParent().pop();
Here is what I am trying to do in my app with react navigation:
There is a default bottom tab navigator.
The app will listen to changes in the data.
If there is a change in the data for any of the screens in the bottom tab navigator, trigger navigation.push() to refresh the component.
What I observed is that the default behavior of the bottom tab navigator is navigation.navigate()...i.e. unless I reload the app, the screens do not refresh themselves.
In short, how do I trigger navigation.push() in the tab navigator? e.g. in the sample code below, how do I set the navigation behavior?
Thanks a lot in advance!
//How do I trigger navigation.push() when each of the bottom tab is pressed?
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Settings" component={SettingsStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
Pulled the following example from my code which solved the issue for me;
import { StackActions } from '#react-navigation/routers';
onPress={() => {
const pushAction =
StackActions.push('ProfileAnimal', { animal_id: item.id });
this.props.navigation.dispatch(pushAction);
this.props.navigation.navigate('ProfileAnimal', {animal_id: item.id});
};
I've a custom navigator because I wanted to go back to the previous screen using a back swipe gesture. Below is the code of the main file from where the navigators are called.
const MainSwipeStack = () => {
return(
<Navigator>
<Route name="LoggedOutHome" component={LoggedOutHome} />
<Route name="SignUp" component={SignUp} />
<Route name="SignupUsername" component={SignupUsername} />
<Route name="Login" component={Login} />
</Navigator>
);
}
export default createSwitchNavigator({
SwipeStack: {screen: MainSwipeStack},
TabHolder: {screen: TabHolder}
}, {
initialRouteName: 'SwipeStack',
headerMode: 'none',
});
And below is the link to the Navigator.js code. (I didn't add the code here because it's a long code.)
https://gist.github.com/shubham6996/a4197d2d0b664d4aabe01091cac6c91e
And the TabHolder takes me to the screen which has createBottomTabNavigator.
So, now I'm not able to navigate from the Login screen to TabHolder stack.
How can I navigate from Login which is in a custom navigator to TabHolder stack?
seems navigation props is not there,
try this
export default withNavigation(Login);
in login and yes do import also
import {withNavigation} from 'react-navigation'
Components which are not directly used in navigators do not have navigation prop by default.
So you need to either pass it as normal props like this,
<Login navigation={this.props.navigation}
but in stack we dont have navigation prop so we can not pass like this (or idk if we have prop there....)
so alternate option is withNavigation and withNaviagtionFocus as shown above
find details about withNavigation