I was looking for a solution to limit the user and do not get out of the application with one click in react native.
import React, {Component} from 'react';
import {BackHandler, View, Dimensions, Animated, TouchableOpacity, Text} from 'react-native';
let {width, height} = Dimensions.get('window');
export default class App extends Component<Props> {
state = {
backClickCount: 0
};
constructor(props) {
super(props);
this.springValue = new Animated.Value(100) ;
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
}
_spring() {
this.setState({backClickCount: 1}, () => {
Animated.sequence([
Animated.spring(
this.springValue,
{
toValue: -.15 * height,
friction: 5,
duration: 300,
useNativeDriver: true,
}
),
Animated.timing(
this.springValue,
{
toValue: 100,
duration: 300,
useNativeDriver: true,
}
),
]).start(() => {
this.setState({backClickCount: 0});
});
});
}
handleBackButton = () => {
this.state.backClickCount == 1 ? BackHandler.exitApp() : this._spring();
return true;
};
render() {
return (
<View style={styles.container}>
<Text>
container box
</Text>
<Animated.View style={[styles.animatedView, {transform: [{translateY: this.springValue}]}]}>
<Text style={styles.exitTitleText}>press back again to exit the app</Text>
<TouchableOpacity
activeOpacity={0.9}
onPress={() => BackHandler.exitApp()}
>
<Text style={styles.exitText}>Exit</Text>
</TouchableOpacity>
</Animated.View>
</View>
);
}
}
const styles = {
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
animatedView: {
width,
backgroundColor: "#0a5386",
elevation: 2,
position: "absolute",
bottom: 0,
padding: 10,
justifyContent: "center",
alignItems: "center",
flexDirection: "row",
},
exitTitleText: {
textAlign: "center",
color: "#ffffff",
marginRight: 10,
},
exitText: {
color: "#e5933a",
paddingHorizontal: 10,
paddingVertical: 3
}
};
Run in snack.expo: https://snack.expo.io/HyhD657d7
I've solved it this way as separated functional Component. This way you don't need to recode it for each app, only include the component in your new App and you've done!
import * as React from 'react';
import {useEffect, useState} from 'react';
import {Platform, BackHandler, ToastAndroid} from 'react-native';
export const ExecuteOnlyOnAndroid = (props) => {
const {message} = props;
const [exitApp, setExitApp] = useState(0);
const backAction = () => {
setTimeout(() => {
setExitApp(0);
}, 2000); // 2 seconds to tap second-time
if (exitApp === 0) {
setExitApp(exitApp + 1);
ToastAndroid.show(message, ToastAndroid.SHORT);
} else if (exitApp === 1) {
BackHandler.exitApp();
}
return true;
};
useEffect(() => {
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
backAction,
);
return () => backHandler.remove();
});
return <></>;
};
export default function DoubleTapToClose(props) {
const {message = 'tap back again to exit the App'} = props;
return Platform.OS !== 'ios' ? (
<ExecuteOnlyOnAndroid message={message} />
) : (
<></>
);
}
=> Only thing you need is to include this component in your App. <=
Because IOS don't have a back-button, IOS don't need this functionality. The above Component automatically detect if Device is Android or not.
By default the Message shown in Toast is predefined in English, but you can set your own one if you add an property named message
to your DoubleTapToClose-Component.
...
import DoubleTapToClose from '../lib/android_doubleTapToClose';
...
return(
<>
<DoubleTapToClose />
...other Stuff goes here
</>
Depending on your Nav-Structure, you have to check if you are at an initialScreen of your App or not. In my case, I have an Drawer with multiples StackNavigatiors inside. So I check if the current Screen is an initial-Screen (index:0), or not. If it is, I set an Hook-Variable which will only be used for those initial-Screens.
Looks like this:
const isCurrentScreenInitialOne = (state) => {
const route = state.routes[state.index];
if (route.state) {
// Dive into nested navigators
return isCurrentScreenInitialOne(route.state);
}
return state.index === 0;
};
...
...
export default function App() {
...
const [isInitialScreen, setIsInitialScreen] = useState(true);
{isInitialScreen && (<DoubleTapToClose message="Tap again to exit app" />)}
...
...
<NavigationContainer
...
onStateChange={(state) => {
setIsInitialScreen(isCurrentScreenInitialOne(state));
}}>
If that description helps you out, don't miss to vote.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With