How to change the direction of the animation in StackNavigator? - animation

How to change the direction of the animation in StackNavigator?
Current Behavior
When user goes to another screen, the screen flies from bottom to top.
Expected Behavior
When user goes to another screen, the screen flies from right to left. (Like Facebook or Instagram!)
StackNavigator Code
export default StackNavigator ({
Main: {
screen: MainScreen,
},
...
}, {
navigationOptions: ({navigation, screenProps}) => ({
tabBarOnPress: blahblaj
}),
lazy: true
// I guess we can do something here
});
If we can set the animation time, it will be even better! Currently it looks like the screen flies from middle of the screen to top. I want natural animation like Facebook or Instagram :)
Thanks in advance,

For react navigation > 5.0:
import {
CardStyleInterpolators,
createStackNavigator,
} from '#react-navigation/stack';
const Stack = createStackNavigator();
export default () => (
<Stack.Navigator
screenOptions={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS
}}
>
<Stack.Screen name="Screen 1" component={ScreenComponent1} />
<Stack.Screen name="Screen 2" component={ScreenComponent2} />
</Stack.Navigator>
);
You may also want to use headerStyleInterpolator: HeaderStyleInterpolators.forUIKit
More info here: https://reactnavigation.org/docs/stack-navigator/#pre-made-configs

For react navigation < 5.0
On iOS it's standard behavior. Android requires a little bit of configuration. There are two options you can use to set screen transitions: mode and transitionConfig. In this case transitionConfig will work:
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStack/CardStackStyleInterpolator';
// this path can be different depending on react-navigation version, this one is for #1.0.0-beta.15
export default StackNavigator ({
Main: {
screen: MainScreen,
},
...
}, {
transitionConfig: () => ({
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
}),
})
We use CardStackStyleInterpolator from react-navigation source, but you can provide custom transition if you want, here is how to make one or here or this article.
mode is more for default behavior:
export default StackNavigator ({
Main: {
screen: MainScreen,
},
...
}, {
mode: 'card',
navigationOptions: ({navigation, screenProps}) => ({
tabBarOnPress: blahblaj
}),
lazy: true
});
mode can have only two values:
card - Use the standard iOS (right to left) and Android (bottom to
top) screen transitions. This is the default.
modal - Make the screens slide in from the bottom which is a common
iOS pattern. Only works on iOS, has no effect on Android.
For react navigation >= 5.0:
import {
CardStyleInterpolators,
createStackNavigator,
} from '#react-navigation/stack';
const Stack = createStackNavigator();
export default () => (
<Stack.Navigator
screenOptions={{
cardStyleInterpolator: CardStyleInterpolators.forHorizontalIOS
}}
>
<Stack.Screen name="Screen 1" component={ScreenComponent1} />
<Stack.Screen name="Screen 2" component={ScreenComponent2} />
</Stack.Navigator>
);
You may also want to use headerStyleInterpolator: HeaderStyleInterpolators.forUIKit
More info here: https://reactnavigation.org/docs/stack-navigator/#pre-made-configs

Updated answer:
import ReactNavigation from "react-navigation";
createStackNavigator({...},{
transitionConfig: () =>
ReactNavigation.StackViewTransitionConfigs.SlideFromRightIOS
})

Here,I just post my answer so that you can change the direction of the animation! That's all! The answer you have accepted is just default!
import CardStackStyleInterpolator from 'react-navigation/src/views/CardStackStyleInterpolator';
export default StackNavigator ({
Main: {
screen: MainScreen,
},
...
}, {
transitionConfig: () => ({
screenInterpolator: CardStackStyleInterpolator.forHorizontal,
}),
});
In this way, the screen transitions will become right to left on both two platforms!
What you need to pay more attention to is you can set any screen transitions whatever you want by using transitionConfig props!

The solution is very simple. In React navigation 4.x you can do like this
import { createAppContainer } from 'react-navigation'
import { createStackNavigator, StackViewTransitionConfigs } from 'react-navigation-stack';
const Navigation = createStackNavigator({
screenA: ComponentA,
screenB: ComponentB,
}, {
mode: 'card',
transitionConfig: () => StackViewTransitionConfigs.SlideFromRightIOS,
}
export const AppNavigation = createAppContainer(Navigation)
Note: You can achieve like this transition in previous react navigation versions also, but you have to change the import

animation​
How the screen should animate when pushed or popped.
Supported values:
default: use the platform default animation
fade: fade screen in or out
fade_from_bottom: fade the new screen from bottom
flip: flip the screen, requires stackPresentation: "modal" (iOS only)
simple_push: default animation, but without shadow and native header transition (iOS only, uses default animation on Android)
slide_from_bottom: slide in the new screen from bottom
slide_from_right: slide in the new screen from right (Android only, uses default animation on iOS)
slide_from_left: slide in the new screen from left (Android only, uses default animation on iOS)
none: don't animate the screen
Only supported on Android and iOS.

Related

React Native Appearance.addChangeListener() does nothing

I am writing my first react-native app and would like to switch themes like GMail does.
Indeed GMail get darker colors when we change the theme mode, or set automatic dark theme during nights, into the phone settings.
So I try to implement the Appearance.addChangeListener as explained in the doc but unfortunately the feature doesn't work.
I am trying on Android 10.
How can I, without restarting the app, update the color of my application when the phone theme change ?
useEffect(() => {
dispatch(changeTheme(Appearance.getColorScheme()));
Appearance.addChangeListener(onThemeChange);
return () => Appearance.removeChangeListener(onThemeChange);
}, []);
// This function is never call
const onThemeChange = ({ colorScheme }) => {
console.log("onThemeChange", colorScheme)
dispatch(changeTheme(colorScheme));
}
I resolved this problem by adding this code to MainActivity.java:
import android.content.res.Configuration;
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getReactInstanceManager().onConfigurationChanged(this, newConfig);
}
and for testing this feature, you can change DarkTheme in Android 10 and above ON/OFF.
useColorScheme
you can use useColorScheme, it will update automatically.
first
import {useColorScheme} from 'react-native;
then, in you functional component
const colorScheme = useColorScheme();
I also suggest to write your own custom hook to update StatusBar and NavigationBar on colorScheme changed
use useColorScheme
It will change the dark and light mode automatically
import {useColorScheme} from 'react-native;
then,for class component
componentDidMount() {
Appearance.getColorScheme()=='dark'?this.setState({ theme: true }): this.setState({ theme: false});
}
componentDidUpdate(){
this.listener =Appearance.addChangeListener((theme) => {
// console.log("theme", theme);
theme.colorScheme == "dark"
? this.setState({ theme: true })
: this.setState({ theme: false });
});
}

Reset react navigation stack history

Update
Changed to use DrawerNavigator as the root for now, but would still like to know if it's possible to use StackNavigator as root but behavior like tabs, or drawers.
Am using reactnavigation v5.
I have the following setup:
<RootStack.Navigator initialRouteName={'SplashNavigator'}>
<RootStack.Screen name={'SplashNavigator'} component={SplashStackNavigator} options={{headerShown: false}} />
<RootStack.Screen name={'DrawerNavigator'} component={DrawerNavigator} />
</RootStack.Navigator>
From SplashNavigator I do a navigation.navigate('DrawerNavigator', {screen:'Home'})
However, the header has a back button that navigates back to the splash screen.
Is there a way to reset the history so it'll take the DrawerNavigator as the main navigator?
Have tried StackActions.replace in HomeScreen but somehow it doesn't recognise the screen.
const HomeScreen = ({navigation}) => {
useEffect(() => {
navigation.dispatch(state => {
console.log(state);
return StackActions.replace('Home')
});
}, []);
...
ExceptionsManager.js:173 The action 'REPLACE' with payload {"name":"Home"} was not handled by any navigator.
Yet I do see the route in the state
"routeNames": [
"Home",
"Promotions"
],
Have tried CommonActions.reset() too but it didn't change anything.
const HomeScreen = ({navigation}) => {
useEffect(() => {
navigation.dispatch(state => {
console.log(state);
return CommonActions.reset({...state, key:null})
});

Null Screen in React Navigation v5?

In React navigation v4 we pass the null screen like this:
`Screen: {
screen: () => null,
navigationOptions: {
tabBarIcon: <AddButton />
}
},`
But How to I can do it in react navigation 5?
I choose this path for the screens I want to hide. May be help you. I manage isLogin with Mobx.
{MenuStore.isLogin ? <></>
: <Drawer.Screen
name="Login"
component={LoginStackScreen}
/>
You can pass <NavigatorName.Screen name="ScreenName">{() => null}</NavigatorName.Screen> to pass null as a screen.
You can use event listener on the tab bar
<Tab.Screen
name="Plus"
component={Add}
listeners={{
tabPress: (e) => {
e.preventDefault(); // — > the main part
},
}}
/>
Where Add is
const Add = () => { return null; };

React navigation Tab in Stack with button in Header

I'd like to have a TabNavigator embedded in a StackNavigator with a button in the Header used to navigate in the main Stack.
Here is a snack of my issue: https://snack.expo.io/#guigui64/tabs-in-stack
The propblem is when I create the TabNavigator:
const TabNavigator = createMaterialTopTabNavigator(
{
ScreenA,
ScreenB,
},
{
navigationOptions: {
headerTitle: 'Title when in tab',
headerRight: (
<Button
onPress={() => this.props.navigation.navigate('C')} // here is the issue !
title="ScreenC"
/>
),
},
}
);
I also tried creating a Component with static navigationOptions and have render() return a TabNavigator. In this case, the header appears fine but not the tabs.
Thanks in advance !

React Material-UI dropdown menu not working

I am creating a simple icon drop-down menu using Material UI. But after rendering the glyph appears and no MenuItems are shown after clicking on the it. Here is the code -
import { IconMenu, IconButton } from 'material-ui' ;
const MoreVertIcon = require('material-ui/lib/svg-icons/navigation/more-vert');
const MenuItem = require('material-ui/lib/menus/menu-item');
const PostCard = React.createClass({
render: function() {
let button = (
<IconButton
touch={true}
tooltip='Click to see menu.'
tooltipPosition='bottom-left'>
<MoreVertIcon />
</IconButton>
);
return (
<IconMenu iconButtonElement={button}>
<MenuItem primaryText="Refresh" />
<MenuItem primaryText="Send feedback" />
<MenuItem primaryText="Settings" />
<MenuItem primaryText="Help" />
<MenuItem primaryText="Sign out" />
</IconMenu>
);
}
});
I had a similar issue. Solution was to to add this somewhere in the app. I'm not sure if it matters where but I added it at a higher-level as possible:
var injectTapEventPlugin = require("react-tap-event-plugin");
injectTapEventPlugin();
Just wanted to add the reason to add injectTapEventPlugin.
According to 300ms tap delay, gone away By Jake Archibald
Browsers stopped waiting 300ms for double taps and react's onClick doesnt give us the proper handle.
and According to react-tap-event-plugin git page
Facebook is not planning on supporting tap events (#436) because browsers are fixing/removing the click delay. Unfortunately it will take a lot of time before all mobile browsers (including iOS' UIWebView) will and can be updated.
Thats why we need to inject the plugin.
About the proper solution, I decided to add the package and inject it in the App's constructor (The higer level I have).
The import:
import injectTapEventPlugin from 'react-tap-event-plugin';
And inside the constructor:
injectTapEventPlugin();
In my case I have to add injectTapEventPlugin as well as change handler.
var injectTapEventPlugin = require("react-tap-event-plugin");
const DropDownMenu = require('material-ui/lib/drop-down-menu');
...
injectTapEventPlugin(); // in constructor
...
handleChange(event, selectedIndex, menuItem) {
this.setState({menu: event.target.value });
}
...
// in render
let menuItems = [
{ payload: '0', text: 'Mon - Sun' },
{ payload: '1', text: 'Mon - Sat' },
{ payload: '2', text: 'Mon - Fri' },
{ payload: '3', text: 'Mon - Fri / Mon - Thu' },
];
...
// in render return
<DropDownMenu menuItems={menuItems} selectedIndex={this.state.menu} onChange={this.handleChange} />

Resources