React Navigation: Applying separate transitions to scenes - react-native

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 }] };
}
},
}),
});

Related

react-navigation enable swipe to back on android

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

How to remove tabbar from the top when I am navigating to another page from one of the tab in react native

Everyone I am facing an issue in react-native as I am new to this .
I am callling a tabbpage from homePage
so in tabpage there is a navbar at the top,below this navbar a tabbar is showing two tabs.
This is good till now but the problem starts from here
In tabPage I have two tabs ->tab1 and tab2
from tab1 I am navigating to page MainPage1 where it is showing a Navbar below navbar a tabbar below tabbar an another navbar. As shown in the picture.
I am totally unable to remove both the top level navbar having title "Stopages" and the tabbar.
I am using Tabview for creating this tabbpage and using stacknavigator for navigating to different pages.I am stuck here and Not able to find solution
NOTE->I have tried using
navigationOptions: {
tabBar: ({ state }) => ({
visible: false
})
but its not doing anything Please help
class TabPage extends React.Component{
state = {
index: 0,
routes: [
{ key: 'Stopagess', title: 'Stopages' },
{ key: 'MapStopagess', title: 'Maps' },
],
};
render() {
return (
<TabView
navigationState={this.state}
renderScene={SceneMap({
Stopagess: Stopages,
MapStopagess: MapStopages,
})
}
renderTabBar={props =>
<TabBar
{...props}
style = {{backgroundColor:'#3f51b5'}}
indicatorStyle={{ color: 'pink' }}
/>
}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get('window').width }}
indicatorStyle={{ backgroundColor: 'pink' }}
/>
);
}
}
This is my Stopages class
class Stopages extends Component {
render()
{
return (
<StopageDetail/>
)
}
}
const StopageDetail = createStackNavigator({
Stp:{
screen: Stpforsomeissue,
navigationOptions: () => ({
header:null,
tabBarVisible: false,
}),
},
NextDetailStopage:{
screen :StopageDetailOfStudents,
navigationOptions: ({ navigation, screenProps }) => ({
title: 'Stopages Detail',
// tabBarVisible: navigation.state.params=false,
headerStyle: { backgroundColor: '#ffd600'},
/>,
})
}
})
I believe you are using createMaterialTopNavigator under a root stack with routeName Stopages and under each Tab route you have a dedicated stack for each one, the first one being the Stopages Detail. Please correct me if thats not the case and edit your post to show the navigation stack you have written.
This might be useful to you:
The root stack can be like the one below:
createStackNavigator ({
StopPages: {screen: MyTabNavigator, navigationOptions: {header: null}}
});
and the TabNavigator will be like this:
const MyTabNavigator = createMaterialTopTabNavigator({
Stopages: {screen: StopagesStack},
Maps: {screen: MapsStack},
});
const StopagesStack = createStackNavigator({
StopagesDetail: {screen: DetailContainer, navigationOptions: {header: null}}
});
The key to hide default stack navigator's header is to set it null in the navigationOptions.
You can play around within navigationOptions within a tab of createMaterialTopTabNavigator in order which ones had TopTabBar or not, Example:
export const Dashboard = createMaterialTopTabNavigator({
First: {
screen: FirstScreen, // This is my first Tab (a View)
navigationOptions: {
title: 'First'
}
},
Second: {
screen: SecondStack, // This is another Stack
navigationOptions: ({ navigation }) => ({
title: 'SecondStack',
tabBarVisible: navigation.state.index === 0, //Tab bar will be visible depending of the state (you can put another index)
animationEnabled: true
})
},
Third: {
screen: ThirdScreen, // This is my third Tab (a View)
navigationOptions: {
title: 'ThirdScreen',
}
}
});

Custom header in nested TabNavigator

I have a fairly complication navigation flow requirement for an app I'm working on.
I have a bottom tab bar, for each tab I'll be having a top tab bar for additional related views.
Which I have working, however on the videos tab in the nested "All" tab, I need to add a search bar to the header, and on the "Favourites" tab I'll be having yet another custom header with an "Edit" button at the top right.
How can I achieve this navigation whilst allowing React Navigation to co-ordinate everything. See images below:
What I don't want to do is disable the header at the MainNavigator level and enable it for particular routes. Or even worse embed the header and the tab bar on individual containers.
routes.js
import {
StackNavigator,
TabNavigator,
DrawerNavigator
} from "react-navigation";
import Feed from "./containers/Feed";
import Auth from "./containers/Auth";
import News from "./containers/News";
import Videos from "./containers/Videos";
import FavouriteVideos from "./containers/FavouriteVideos";
const DashboardNavigator = TabNavigator(
{
Feed: {
screen: Feed
},
News: {
screen: News
}
},
{
tabBarPosition: "top"
}
);
const VideoNavigator = TabNavigator(
{
Videos: {
screen: Videos,
navigationOptions: {
title: "All"
}
},
Favourites: {
screen: FavouriteVideos
}
},
{
tabBarPosition: "top"
}
);
const MainNavigator = TabNavigator(
{
Dashboard: {
screen: DashboardNavigator,
navigationOptions: ({}) => ({
title: "Dashboard"
})
},
Video: {
screen: VideoNavigator,
navigationOptions: ({}) => ({
title: "Videos"
})
}
},
{
swipeEnabled: false,
animationEnabled: false,
tabBarPosition: "bottom"
}
);
const AuthenticatedNavigator = DrawerNavigator({
App: {
screen: MainNavigator
}
});
const RootNavigator = StackNavigator({
LoggedOut: {
screen: Auth
},
Authenticated: {
screen: AuthenticatedNavigator
}
});
export default RootNavigator;
Snack
https://snack.expo.io/H1qeJrLiM
Images
You can use react-navigation addListener function with combination setParams to achieve desired behavior.
You can listen for focus and blur events and then change a parameter. Then in your route config you can look for this parameter and decide what to render for header. I changed your snack to show a working example of what I am suggesting.
Example
const MainNavigator = TabNavigator(
{
Dashboard: {
screen: DashboardNavigator,
navigationOptions: ({}) => ({
title: "Dashboard"
})
},
Video: {
screen: VideoNavigator,
navigationOptions: ({navigation}) => {
let title = 'Videos';
navigation.state.routes.forEach((route) => {
if(route.routeName === 'Videos' && route.params) {
title = route.params.title;
}
});
// I set title here but you can set a custom Header component
return {
tabBarLabel: 'Video',
title
}
}
}
},
{
swipeEnabled: false,
animationEnabled: false,
tabBarPosition: "bottom"
}
);
export default class Videos extends Component {
constructor(props) {
super(props);
this.willFocusSubscription = props.navigation.addListener(
'willFocus',
payload => {
this.props.navigation.setParams({title: 'All Videos'});
}
);
this.willBlurSubscription = props.navigation.addListener(
'willBlur',
payload => {
this.props.navigation.setParams({title: 'Just Videos'});
}
);
}
componentWillUnmount() {
this.willFocusSubscription.remove();
this.willBlurSubscription.remove();
}
render() {
return (
<View>
<Text> Videos </Text>
</View>
);
}
}

My React-Native navigation just wont style

I have an issue that no matter what I try, I just cannot get styling to work on my Stack with Drawer navigator. I've tried several examples and solutions but just none of them work, my navigation stays the default blue.
In my current code below, I've left 2 coloring options open - those two are of the many things I've tried before.
I hope that someone knows what's going on because I'm starting to get clueless.
const MainScreenNavigator = StackNavigator({
'Home': {
screen: Components.HomeScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Home' navigation={navigation} />
})
},
'Scan': {
screen: Components.ScanScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Scan QR' navigation={navigation} />
})
},
'LockInfo': {
screen: Components.LockInfoScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Lock' navigation={navigation} />
})
},
});
const RootNavigator = DrawerNavigator ({
Home: {
screen: MainScreenNavigator,
},
});
RootNavigator.navigationOptions = {
contentOptions: {
activeBackgroundColor: '#ff5976',
style: {
backgroundColor: '#000000'
}
}
}
const ModalNavigator = StackNavigator(
{
Main: { screen: Main },
Login: { screen: Login },
},
{
headerMode: 'none',
mode: 'modal',
navigationOptions: {
gesturesEnabled: false,
},
https://reactnavigation.org/docs/navigators/stack#Screen-Navigation-Options
checkout this above link, search and add the required styling properties inside your own StackNavigator, headerStyle, headerTitleStyle, headerBackTitleStyle, headerTintColor and so on...
Also for DrawerNavigator, checkout this below link and replicate the code, styling properties for the same are provided.
https://reactnavigation.org/docs/navigators/drawer#DrawerNavigator
Also remove the single inverted commas from Home, Scan and LockInfo

Reset react-navigation Stack from Drawer

A lot of issues opened on official react-navigation github: https://github.com/react-community/react-navigation/issues but no one gives a real solution, including one post saying "look at this problem, a lot of issues": https://github.com/react-community/react-navigation/issues/691 without answer.
Basically: I have a DrawerNavigator with some nested StackNavigator.
export const MainNavigator = DrawerNavigator(
{
OrderNew: {
screen: OrderNew,
navigationOptions: {
drawerLabel: DrawerLabel('New Order')
}
},
Orders: {
screen: OrderNavigator, //a StackNavigator
navigationOptions: {
drawerLabel: DrawerLabel('Orders')
}
},
MenuNavigator: {
screen: MenuNavigator, //a StackNavigator
navigationOptions: {
drawerLabel: DrawerLabel('Menu')
}
},
//more screens...
{
initialRouteName: 'Orders',
drawerPosition: 'right',
contentComponent: props => <ScrollView style={{backgroundColor: 'rgba(227, 100, 29, .95)'}}><DrawerMenu state={props} /></ScrollView>,
}
);
So, I'm passing a contentComponent with my own menu component. My idea is to overwrite the onItemPress event of DrawerItems component and put my logic here.
What I want is: I would like to reset MenuNavigator (and other stack screens) every time users press that item. I just can't achieve this. As i said, I tried a lot of code. Actually, it's like this:
onItemPress={
router => {
const navigateAction = NavigationActions.navigate({
routeName: router.route.routeName,
});
this.props.state.navigation.dispatch(navigateAction);
}
}
The above code works, I can navigate through drawer items. Now, in my head, I just should pass an subaction resetting to the first item on navigator. So I tried:
onItemPress={
router => {
const navigateAction = NavigationActions.navigate({
routeName: router.route.routeName,
action: NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({routeName: router.route.routes[0].routeName}),
]
})
});
this.props.state.navigation.dispatch(navigateAction);
}
}
And explodes an error:
There is no route defined for key Menu.
Must be one of: 'Orders','OrderClient'
Summarizing: how could I reset StackNavigator when I do navigate to another DrawerNavigator's item?
You are missing key: null.
Navigation actions should be like this.
action: NavigationActions.reset({
index: 0,
key: null,
actions: [
NavigationActions.navigate({routeName: router.route.routes[0].routeName}),
]
})