react-navigation enable swipe to back on android - react-native

I have a stack navigation and I want to enable swipe to go back on both android and IOS
there is my code
import {
createStackNavigator,
StackViewTransitionConfigs,
} from "react-navigation-stack";
import HomeScreen from "../../screens/Home/Home";
import CategoryScreen from "../../screens/Category/Category";
import SubCategoryScreen from "../../screens/SubCategory/SubCategory";
import ProductScreen from "../../screens/Product/Product";
const ShopStack = createStackNavigator(
{
Shop: {
screen: HomeScreen,
navigationOptions: {
gesturesEnabled: true,
},
},
Category: {
screen: CategoryScreen,
navigationOptions: {
gesturesEnabled: true,
},
},
SubCategory: {
screen: SubCategoryScreen,
},
Product: {
screen: ProductScreen,
navigationOptions: {
gesturesEnabled: true,
},
},
},
{
headerMode: "none",
transitionConfig: () => StackViewTransitionConfigs.SlideFromRightIOS,
defaultNavigationOptions: {
gesturesEnabled: true,
},
},
);
export default ShopStack;
the expected behavior is when swipe go back on android like ios
react-navigation version 4

As for v5, v6 there is an easier solution, just put those values to screenOptions in your Stack.Navigator
gestureDirection: 'horizontal',
gestureEnabled: true,
...
<Stack.Navigator
screenOptions={{
gestureDirection: 'horizontal',
gestureEnabled: true,
}}>
Those options will enable gestures on android, as well as direction will be 'horizontal' now, which is 'vertical' by default on android.

This is how I made it work, hope it works for you as well.
Couldn't have managed to get the same result on react-navigation 5
const AppStack = createStackNavigator({
FolderListScreen: {
screen: FolderListScreen,
navigationOptions: ({ navigation }) => ({
// title: 'This is title'
header: null
})
},
Edit: {
screen: EditScreen,
navigationOptions: ({ navigation }) => ({
header: null
})
},
FoldersListModal: {
screen: FoldersListModal
},
WebViewScreen: {
screen: WebViewScreen
},
},
// DOCUMENTATION
// https://reactnavigation.org/docs/en/stack-navigator.html#stacknavigatorconfig
{
headerMode: 'none',
mode: 'card',
defaultNavigationOptions: {
gesturesEnabled: true,
// if you want to change the back swipe width
//just put the number, e.g. 100 would be fine to get the iOS effect
gestureResponseDistance: {
horizontal: Dimensions.get('window').width
}
},
transitionConfig: () => ({
transitionSpec: {
duration: 300,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
},
screenInterpolator: sceneProps => {
const { layout, position, scene } = sceneProps;
const { index } = scene;
// const height = layout.initHeight;
const width = layout.initWidth;
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [width, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateX }] };
},
})
});
Edit:
I made it work on v5 as well 💪
https://snack.expo.io/#ukiand1/react-navigation-5---android-swipe-back-bug

Related

I have a problem in my code when updating react-navigation v2 to v3

I want to update the react-navigation library V2 to V3 and change part of my code thinking that there would not be any problems but it turns out that I have problems creating createStackNavigator with a screen of type createDrawerNavigator and that in turn contains createBottomTabNavigator.
my code that works with the previous version was:
export const createRootNavigator = (signedIn = false) => {
const commonNavigationOptions = {
headerStyle: {
shadowColor: 'transparent',
elevation: 0
},
headerTintColor: DEFAULT_THEME.topaz
};
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer('Home'),
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.backgrouncolorHomeSafeArea)
})
},
Cards: {
screen: Tabs('Cards'),
navigationOptions: () => ({
headerStyle: {
height: 0
}
})
},
);
const SignedOut = createStackNavigator(
{
SignIn: {
screen: LoginContainer,
navigationOptions: () => ({
headerStyle: {
height: 0
},
header: getSafeArea(DEFAULT_THEME.dark)
})
},
SelectableCardsList: { screen: SelectableCardsListComponent },
);
return createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
);
};
const Drawer = (initialRoute) => createDrawerNavigator(
{
Home: { screen: Tabs('Home') },
{
initialRouteName: initialRoute,
contentComponent: CustomDrawerComponent
}
);
const Tabs = (initialRouteName) => createBottomTabNavigator(
{
Home: {
screen: HomeContainer,
navigationOptions: {
tabBarLabel: I18n.t('tabs.me')
}
},
Home2: {
screen: Home2,
navigationOptions: {
tabBarLabel: I18n.t('tabs.credentials')
}
},
{
initialRouteName: initialRouteName,
tabBarComponent: ({ navigation }) => <CustomBottomBarComponent navigation={navigation} navigationState={navigation['state']} />,
tabBarOptions: {
style: {
backgroundColor: 'white'
}
}
}
);
try this solution with react-navigation V3 and send me an error
I try the following:
encapsulate createSwitchNavigator in createAppContainer and separate (SignedOut and SignedOut) createStackNavigator out of createSwitchNavigator, the rest is still the same.
export const createRootNavigator = (signedIn = false) => createAppContainer(createSwitchNavigator(
{
SignedIn: { screen: SignedIn },
SignedOut: { screen: SignedOut }
},
{
initialRouteName: signedIn ? 'SignedIn' : 'SignedOut'
}
));
I get the following error: The component for route 'Home' must be a react component. For Example
import MyScreen from './MyScreen';
...
Home : MyScreen,
}
you can also use a navigator:
The problem is located in this part:
const SignedIn = createStackNavigator(
{
Home: {
screen: Drawer,
Also try to change Drawer for any component (a component with a blank screen) and this works, but I can not insert the Tabs in the Drawer.
Thank you very much for your help.

How can I hide tab bar navigation?

How can I hide tabbar navigation? After navigate I can See tabbar and not working but in working.
navigationOptions:{tabBarVisible: false} On line 5 not working but on line 22 for main tabs working fine.
const MenStack = createStackNavigator({
menStackNav: { screen: MenTabScreen},
Products: {
screen: ProductsShow,
navigationOptions:{tabBarVisible: false},
},
},{
initialRouteName: 'menStackNav',
headerMode: 'none',
navigationOptions: {
headerVisible: false,
}
});
const HomeScreenTabs = createMaterialTopTabNavigator({
home:{
screen:HomeTabScreen,
},
women: WomenTabScreen,
men: {
screen:MenStack,
navigationOptions:{tabBarVisible: false},
},
},{
tabBarOptions: {
activeTintColor: '#fff',
inactiveTintColor: '#eee',
tabStyle:{backgroundColor:'#0077FF',height:40},
labelStyle: {
fontSize: 14,
fontFamily:'iransans_medium',
},
},
initialRouteName: 'men',
mode: 'modal',
headerMode: 'none',
});
I have 3 tabs and a stack navigation for navigate to another screen to show products. I need to hide tabbar when products are visible.
You need to use tabBarVisible to hide the tabBar inside your stackPage
const MenStack = createStackNavigator({
Home:{screen: MenTabScreen,},
Products:{screen: ProductsShow,}
}, {initialRouteName: 'Home', headerMode: 'none')}
MenStack.navigationOptions = ({navigation}) => {
let tabBarVisible = true;
if(navigation.state.index > 0){
tabBarVisible = false;
}
return {
tabBarVisible,
}
}
const HomeScreenTabs = createBottomTabNavigator({
Home:{screen: HomeTabScreen,},
Women :{screen: WomenTabScreen,},
Men : {screen : MenStack,}
})
export default HomeScreenTabs;

Modal navigation for certain screens in react-native app

For 3 screens in my app I need to make a modal transition.
For the rest of the screens - I need the default right-to-left transition.
How do I achieve this with different stack navigators?
const AuthStack = createStackNavigator(
{
LogIn: { screen: LogInScreen },
Signup: { screen: SingupScreen },
Welcome: { screen: WelcomeScreen },
CodeVerification: { screen: CodeVerificationScreen },
PasswordSelection: { screen: PasswordSelectionScreen },
RegistrationSuccess: { screen: RegistrationSuccessScreen }
},
{
initialRouteName: 'LogIn'
}
)
const ModalNavigator = createStackNavigator({
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'ContactInfoEdit',
})
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
Modal: ModalNavigator
},
{
initialRouteName: 'Products',
mode: 'modal',
}
)
If I set mode: modal it will make all the navigation animations will be modal.
If I remove it, all the navigations will be default (right-to-left)
const ProductsTabStack = createBottomTabNavigator(
{
Orders: { screen: OrdersScreen },
Products: { screen: ProductsStack },
Profile: { screen: ProfileScreen }
},
{
initialRouteName: 'Products',
backBehavior: 'none',
tabBarOptions: {
activeTintColor: '#ffffff',
inactiveTintColor: primaryColor,
activeBackgroundColor: primaryColor,
labelStyle: {
marginBottom: 17,
fontSize: 15,
},
tabStyle: {
shadowColor: primaryColor,
borderWidth: 0.5,
borderColor: primaryColor,
},
},
},
)
export const AppNavigator = createSwitchNavigator({
Auth: AuthStack,
Categories: ProductsTabStack
})
I tried setting mode: modal in the ModalNavigator, but then it took the default parent navigation.
You probably want to try to use StackNavigatorConfig while navigating to that screen this.props.navigation.navigate('ScreenName', params, {mode: 'modal'})
If you want to keep all your transition code in same file as you have right now, you can do the same as what react-navigation is suggesting here
It goes something like that
import { createStackNavigator, StackViewTransitionConfigs } from 'react- navigation';
/* The screens you add to IOS_MODAL_ROUTES will have the modal transition. */
const IOS_MODAL_ROUTES = ['OptionsScreen'];
let dynamicModalTransition = (transitionProps, prevTransitionProps) => {
const isModal = IOS_MODAL_ROUTES.some(
screenName =>
screenName === transitionProps.scene.route.routeName ||
(prevTransitionProps && screenName ===
prevTransitionProps.scene.route.routeName)
)
return StackViewTransitionConfigs.defaultTransitionConfig(
transitionProps,
prevTransitionProps,
isModal
);
};
const HomeStack = createStackNavigator(
{ DetailScreen, HomeScreen, OptionsScreen },
{ initialRouteName: 'HomeScreen', transitionConfig: dynamicModalTransition }
);
OK, found a workaround for custom transitions in 1 StackNavigator using https://www.npmjs.com/package/react-navigation-transitions:
const handleCustomTransition = ({ scenes }) => {
const nextScene = scenes[scenes.length - 1]
if (nextScene.route.routeName === 'ContactInfoEdit')
return fromBottom()
else
return fromRight()
}
const ProductsStack = createStackNavigator(
{
Products: {
screen: ProductsScreen
},
Product: {
screen: ProductScreen
},
ProductBuy: {
screen: ProductBuyScreen
},
OrderConfirm: {
screen: OrderConfirmScreen
},
PlaceOrder: {
screen: PlaceOrderScreen
},
ContactInfoEdit: { screen: ContactInfoEditScreen },
DeliveryAddressEdit: { screen: DeliveryAddressEditScreen },
OrderPlacedScreen: { screen: OrderPlacedScreen },
},
{
initialRouteName: 'Products',
transitionConfig: (nav) => handleCustomTransition(nav)
}
)

How to add or remove tab item in bottom tab?

I have issue with react-navigation v3. I want to add new component (tab item) or remove exists tab item in the current tab bar.
I have do:
export const MainTabContainer = createBottomTabNavigator(
{
Genre: {
screen: GenreScreen
},
History: {
screen: HistoryScreen
}
},
{
initialRouteName: 'Genre',
backBehavior: true,
lazy: true,
tabBarOptions: {
activeTintColor: '#454545',
activeBackgroundColor: 'white',
inactiveTintColor: '#ccc',
inactiveBackgroundColor: 'white'
}
}
);
const defaultTabGetStateForAction = MainTabContainer.router.getStateForAction;
const defaultTabGetComponentForRouteName = MainTabContainer.router.getComponentForRouteName;
MainTabContainer.router.getStateForAction = (action, state) => {
if (!state || !action || !action.name) {
return defaultTabGetStateForAction(action, state);
}
if (action.name === 'addTab') {
const routes = [
...state.routes,
{key: action.key, routeName: action.routerName, params: action.data}
];
return {
...state,
routes,
index: routes.length - 1,
};
}
return defaultTabGetStateForAction(action, state);
};
MainTabContainer.router.getComponentForRouteName = (routeName) => {
if (routeName === 'Recent') return RecentScreen;
else if (routeName === 'About') return AboutScreen;
return defaultTabGetComponentForRouteName(routeName);
};
export const Home = createDrawerNavigator(
{
Main: MainTabContainer
},
{
drawerPosition: 'left',
useNativeAnimations: true,
drawerType: 'front',
contentComponent: MenuDrawer
}
);
const RootStack = createStackNavigator(
{
SplashScreen: SplashScreen,
Home: Home,
DetailScreen: DetailScreen
},
{
initialRouteName: 'SplashScreen',
mode: 'modal',
headerMode: 'none',
header: null,
transparentCard: true,
cardStyle: {
shadowColor: 'transparent',
backgroundColor:"transparent",
opacity: 1
}
}
);
export const AppContainer = createAppContainer(RootStack);
This way will return error not found the routerName GenreScreen or AboutScreen.
When the user have done something, I want to add GenreScreen or AboutScreen or remove one of them on it. GenreScreen and AboutScreen is not display normally.
Have any suggest to solve this issue?
The first, thank for time.

React Navigation: Applying separate transitions to scenes

I want to apply different transitions to scenes in my StackNavigator in React Navigation, for my React Native (iOS) app.
export const AppNavigator = StackNavigator({
Home: { screen: Home },
NewChild: { screen: NewChild },
Journal: { screen: Journal }
});
I might want NewChild to load from the bottom up, like a modal. Or I might want it to slide in from right to left. It would great if this could be customised. From the documentation, it seems you can only set your StackNavigation to either card or modal for the whole thing.
Thanks to everyone in the thread, it worked for me.
I have many scenes managed by only one StackNavigator and I wanted to make a different animation for some of them.
First of all, I made myself a constant in my router which included every scenes I wanted to display with horizontal transition :
const horizontalTransitionScenes = [
AddContact,
];
After that, I designed my app router with StackNavigator :
const App = StackNavigator({
AddContact: {
screen: AddContact,
},
EditContact: {
screen: EditContact,
},
});
I used CardStackStyleInterpolator to modify the transitions between scenes. I can be imported like this : import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
We can now add transitionConfig params to our StackNavigator to display a different animation depending on our scenes :
const App = StackNavigator(
{
AddContact: {
screen: AddContact
},
EditContact: {
screen: EditContact
}
},
{
transitionConfig: (currentState: any) => {
if (
currentState.scenes[
currentState.scenes.length - 1
].route.routeName.includes(horizontalTransitionScenes)
) {
return {
screenInterpolator: CardStackStyleInterpolator.forHorizontal
};
}
return {
screenInterpolator: CardStackStyleInterpolator.forVertical
};
}
}
);
With that StackNavigator implementation, you will be able to display different kind of animations for the scenes you want.
Have a good day 🎉
As you already saw in the docs, it's not possible right now. You can track this issue and this issue requesting this feature. It's on the roadmap for v2, but since v1 isn't even released yet, your best bet is probably to either:
Implement your own transitionConfig that somehow handles this per screen. I'm not sure if this is possible.
Fork an implement something using the ideas from the main issue. Or simple make your fork specific to your code and maintain it until that issue is resolved.
I was able to accomplish this by doing some conditional logic, we're on react-navigation 3.3.2.
You have to import Easing and Animated from react-native and then you can conditionally render transitions for a specific screen by name, or also by index (below is by name).
By declaring the config outside the navigator, and toggling transitionSpec with an {} it will only trigger on that particular screen -- same with the conditional for screenInterpolator.
Screen1 to Screen2 has the animation, but none of the other screens have that animation.
const screen2Config = {
duration: 300,
easing: Easing.out(Easing.poly(4)),
timing: Animated.timing,
};
export const ScreenStack = createStackNavigator({
Screen1: {
screen: Screen1,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen1',
headerTitleAllowFontScaling: false,
}),
},
Screen2: {
screen: Screen2,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen2',
headerTitleAllowFontScaling: false,
tabBarVisible: false,
}),
},
Screen3: {
screen: Screen3,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen3',
headerTitleAllowFontScaling: false,
}),
},
Screen4: {
screen: Screen4,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Screen4',
headerTitleAllowFontScaling: false,
}),
},
}, {
headerMode: 'float',
mode: 'modal',
transitionConfig: sceneProps => ({
transitionSpec: sceneProps.scene.route.routeName === 'Screen2' ? screen2Config : {},
screenInterpolator: (sceneProps) => {
if (sceneProps.scene.route.routeName === 'Screen2') {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const width = layout.initWidth;
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [width, 0, 0],
});
const opacity = position.interpolate({
inputRange: [index - 1, index - 0.99, index],
outputRange: [0, 1, 1],
});
return { opacity, transform: [{ translateX }] };
}
},
}),
});