React Native Navigation different animation depending on origin - react-native

I'm trying to get the following to work.
There are 3 screens:
Screen A
Screen B
Screen C
I want a different animation for when A goes to C than when B goes to C. Does anyone know how to do that?

You can use this library : rn-transitions . SO as per the docs you can add specific transitions to specific pages like :
import { fromLeft, zoomIn, zoomOut } from 'react-navigation-transitions'
const handleCustomTransition = ({ scenes }) => {
const prevScene = scenes[scenes.length - 2];
const nextScene = scenes[scenes.length - 1];
// Custom transitions go there
if (prevScene
&& prevScene.route.routeName === 'ScreenA'
&& nextScene.route.routeName === 'ScreenB') {
return zoomIn();
} else if (prevScene
&& prevScene.route.routeName === 'ScreenB'
&& nextScene.route.routeName === 'ScreenC') {
return zoomOut();
}
return fromLeft();
}
const PrimaryNav = createStackNavigator({
ScreenA: { screen: ScreenA },
ScreenB: { screen: ScreenB },
ScreenC: { screen: ScreenC },
}, {
transitionConfig: (nav) => handleCustomTransition(nav)
})
hope this helps. feel free for doubts

Related

How can I use custom screen to screen transitions with react-navigation 4+?

I need to vary my transition based on the screen, so I had this before react-navigation 4:
const HomeStack = createStackNavigator({
Home: HomeScreen,
AScreen,
BScreen,
CScreen,
DScreen,
EScreen,
},
{
headerMode: 'none',
transitionConfig: (nav) => handleCustomTransition(nav),
}
Then something like:
const handleCustomTransition = ({scenes}) => {
const prevScene = scenes[scenes.length - 2];
const nextScene = scenes[scenes.length - 1];
const duration = 500;
if (prevScene
&& prevScene.route.routeName === 'AScreen'
&& nextScene.route.routeName === 'BScreen') {
return fromBottom(duration);
} else if (prevScene
&& prevScene.route.routeName === 'AScreen'
&& nextScene.route.routeName === 'CScreen') {
return fromBottom(duration);
}
return fromRight();
};
So if you're going A->B or A->C the animation is fromBottom() but if you're doing any other transition its fromRight().
Any suggestions on how to update this to the new style? I've read the docs which don't cover per-screen transitions and other pages are incomplete.
Make sure you have installed these versions specifically
"react-navigation": "^4.0.10",
"react-navigation-redux-helpers": "^4.0.1",
"react-navigation-stack": "^1.10.3",
Because in higher versions of react navigation there are issues with custom transitions.Now in your file where you are creating stackNavigator.Do it as.
const RouteConfigs = {
splash: {
screen: SplashScreen
},
};
const handleCustomTransition = ({ scenes }) => {
const nextScene = scenes[scenes.length - 1].route.routeName;
switch (nextScene) {
case "splash":
Utils.enableExperimental();
return Utils.fromBottom();
default:
return false;
}
};
const NavigatorConfigs = {
navigationOptions: {
gesturesEnabled: false
},
headerMode: "none",
transitionConfig: screen => {
return handleCustomTransition(screen);
},
initialRouteName: "splash"
};
const AppNavigator = createStackNavigator(RouteConfigs, NavigatorConfigs);
export default createAppContainer(AppNavigator);
In these lines I've written my own custom animations.So you can define your own.
Utils.enableExperimental();
return Utils.fromBottom();

App exit on back click on android in react native?

on Android Mi Note 3, hardware back button is not fire the handleBackPress , when I will click on back the app exit.
I have do the following code but the handleBackPress is not called.
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
handleBackPress = () => {
this.goBack(); // works best when the goBack is async
return true;
}
Navigation Code :
const ModalSignUp = createStackNavigator(
{
Signup: { screen: Signup, key: 'Signup' },
PartyList: { screen: PartyList, key: 'PartyList' },
StatesList: { screen: StatesList, key: 'StatesList' },
},
{
initialRouteName: 'Signup',
headerMode: 'none',
mode: 'card',
}
);
Navigate :
this.props.navigation.push("StatesList")
Expected :
back click on hardware button, go to previous screen.
Your error can be in the way you get the next view of react-navigation.
You need to use .push to create a new view on the stack and when you click the back button, the .goBack() will be triggered.
By default, back button will always make the navigation to go back on the stack, but if you have only one view in the stack (this happens when you only use .navigate) the app will exit.
Not sure how you are navigating through the views, but this can be a solution.
Edit: To solve this problem, when navigating through views, use navigation.push('viewname') instead of navigation.navigate('viewname'). You don't need any other method (like the one you put in the question).
Also check the docs to understand the how navigating works or this question
Try using return false instead of return true.
1. Import
import { BackHandler, DeviceEventEmitter } from 'react-native'
2. constructor
constructor(props) {
super(props)
this.backPressSubscriptions = new Set()
}
3. Add and Remove Listeners
componentDidMount() {
DeviceEventEmitter.removeAllListeners('hardwareBackPress')
DeviceEventEmitter.addListener('hardwareBackPress', () => {
let invokeDefault = true
const subscriptions = []
this.backPressSubscriptions.forEach(sub => subscriptions.push(sub))
for (let i = 0; i < subscriptions.reverse().length; i += 1) {
if (subscriptions[i]()) {
invokeDefault = false
break
}
}
if (invokeDefault) {
BackHandler.exitApp()
}
})
this.backPressSubscriptions.add(this.handleHardwareBack)
}
componentWillUnmount() {
DeviceEventEmitter.removeAllListeners('hardwareBackPress')
this.backPressSubscriptions.clear()
}
4. Handle back
handleHardwareBack = () => {
this.props.navigation.goBack(null)
console.log(" ********** This is called ************ ");
return true;
}
Try this:
import {BackHandler} from 'react-native';
export default class Component extends Component {
_didFocusSubscription;
_willBlurSubscription;
constructor(props) {
super(props);
this._didFocusSubscription = props.navigation.addListener('didFocus',payload =>
BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
);
}
}
componentDidMount() {
this._willBlurSubscription = this.props.navigation.addListener('willBlur', payload =>
BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid)
);
}
componentWillUnmount() {
this._didFocusSubscription && this._didFocusSubscription.remove();
this._willBlurSubscription && this._willBlurSubscription.remove();
}
onBackButtonPressAndroid = () => {
//code when you press the back button
};
Give it a try... this one works for me: in componentWillUnmount
BackHandler.removeEventListener('hardwareBackPress', () => {});
Also, make sure in each case you check in your this.goBack(); it return something
goback = () => {
if (condition2)
// handling
return something;
if (condition2)
// handling
return something;
// default:
return true;
};

Tab screens popping up from the bottom

Does anyone know how to fix tab screens sliding up from the bottom?
Each tab screen is inside its own Stack Navigator:
export const Tabs = createBottomTabNavigator(
{
Home: {
screen: HomeStack
},
Profile: {
screen: ProfileStack
}
},
{
defaultNavigationOptions: ({ navigation }) => ({
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
var tabImage = null;
if (routeName == "Home") {
tabImage = require("../../../assets/icons/my-story.png");
} else if (routeName == "Profile") {
tabImage = require("../../../assets/icons/my-plan.png");
}
return <Image source={tabImage} style={focused ? null : { opacity: 0.8 }} />;
}
}),
animationEnabled: false
}
);
And the stacks are setup as per according to the React Navigation docs:
export const HomeStack = createStackNavigator(
{
Home: Home
}
);
The screens are popping up from the bottom after they have been lazy loaded. When they are first loaded they fade in:
For anyone experiencing this problem I fixed it by installing React Native Screens
https://github.com/kmagiera/react-native-screens

Set initialRouteName dynamically in React Navigation TabNavigator

I want to set initialRouteName of my TabNavigator dynamically in splash screen. In splash, i decide initialRouteName based on an api result but i don't know how to pass this route name to my router file.
Router.tsx:
public static Routes = StackNavigator({
Splash: { screen: Splash },
Verification: { screen: Verification },
Login: { screen: Login },
Main: {
screen: TabNavigator({
Calendar: { screen: Calendar },
PendingReserves: { screen: PendingReserves },
Profile: { screen: Profile },
},
{initialRouteName: 'Calendar'}) // I want to set it from splash
}
}, {initialRouteName: 'Splash'} );
splash.tsx:
constructor(props: SplashScreenProps, context: any) {
super(props, context);
this.state = {
Error: false
}
this.check()
}
check = async () => {
let pending = await dataService.getPendingReserves(data);
if (pending.Success) {
if (pending.result.length > 0) {
// go to pendingReserve Tab
} else {
// go to Calendar Tab
}
} else {
this.setState({ Error: true })
}
}
I tried to route statically to PendingReserves and in it's constructor, check the api and change tab if necessary but i couldn't change tab programmatically with this code:
pendingReserves.tsx:
fetchData = async () => {
let res = await Utilities.getPendingReserves()
if (res && res.length > 0) {
...
} else {
const resetAction = NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: 'Calendar' })]
})
this.props.navigation.dispatch(resetAction)
// this.props.navigation.navigate({ routeName: 'Calendar' })
// both reset and simple navigate are not works correctly...
}
}
So any solution for dynamically set initialRouteName OR change tab programmatically will help me. Thanks
I'm using:
"react-native": "0.49.3",
"react-navigation": "^1.0.0-beta.13"
Finally... I didn't find a way to change tab programmatically in react navigation version 1, so i update to version 2 and this.props.navigation.navigate('Calendar') works correctly.

React-native: Go back to a specific screen in stack

This is the root navigator
export const AppNavigator = StackNavigator({
Splash: { screen: Splash },
Dashboard: { screen: DashboardDrawer }
});
const DashboardDrawer = DrawerNavigator({ DashboardScreen: {
screen: StackNavigator({
A: { screen: A },
B: { screen: B },
C: { screen: C },
D: { screen: D },
}
}, {
contentComponent: DashboardDrawerComponent,
drawerWidth: 280
});
I have 4 screens - A, B, C, D in my stack.
I want to jump from D to A. (or D to any screen)
I referred to the following react-navigation documentation-https://reactnavigation.org/docs/navigators/navigation-prop#goBack-Close-the-active-screen-and-move-back
The above doc. states that to go from screen D to screen A (popping D, C, and B) you need to supply a key to goBack FROM, in my case B, like this
navigation.goBack(SCREEN_KEY_B)
So, my question is from where should I get the key for a specific screen?
I checked my root navigation object, and it shows me some dynamically generated key for each screen. How can I specify my own keys for the screens?
It was tricky!
I referred to this section of react-navigation documentation, and have achieved the above!
https://reactnavigation.org/docs/routers/#Custom-Navigation-Actions
And this is how,
1. Altered my DrawerNavigator in the question, a little (to fit in the the following stacknavigator)
const DrawerStackNavigator = new StackNavigator({
A: { screen: A },
B: { screen: B },
C: { screen: C },
D: { screen: D },
}
});
const DashboardDrawer = DrawerNavigator({
DashboardScreen: DrawerStackNavigator,
}, {
contentComponent: DashboardDrawerComponent,
drawerWidth: 280
});
2. Dispatched an action in screen D
const {navigation} = this.props;
navigation.dispatch({
routeName: 'A',
type: 'GoToRoute',
});
3. Listened to this action on my stack navigator
const defaultGetStateForAction = DrawerStackNavigator.router.getStateForAction;
DrawerStackNavigator.router.getStateForAction = (action, state) => {
if (state && action.type === 'GoToRoute') {
let index = state.routes.findIndex((item) => {
return item.routeName === action.routeName
});
const routes = state.routes.slice(0, index+1);
return {
routes,
index
};
}
return defaultGetStateForAction(action, state);
};
Looks like the way they are doing it right now is to save the key of the screen you want to come back through params. So once you are in B and navigate to C, you pass the screen key to C as a param and then when you navigate from C to D you pass the key of B to D as a param.
More info here.
I use a setup with NavigatorService as outlined here in the post from unexge.
From the service, I expose the following:
function goBackToRouteWithName(routeName: string) {
if (!_container) return;
const route = _getRouteWithName(_container.state.nav, routeName);
route && _container.dispatch(NavigationActions.back({ key: route.key }));
}
function _getRouteWithName(route, name: string): ?NavigationRouteConfigMap {
const stack = [];
stack.push(route);
while (stack.length > 0) {
let current = stack.pop();
if (current.routes) {
for (let i = 0; i < current.routes.length; i++) {
if (current.routes[i].routeName === name) {
//NOTE because of going from, not to!!
return current.routes[i + 1];
}
stack.push(current.routes[i]);
}
}
}
}
this works well for me.