I am trying to go back when the user presses back button on Android.
I have added listener to the screen and it's receiving event when in the remotely debug mode. But it's not working properly when I don't do debug remotely. It's really weird.
I am gonna attach code snippets that I have written.
//Navigator
const BoardNavigator = StackNavigator({
Board: { screen: Board }
});
//Board Component
class Board extends Component {
componentWillMount () {
BackHandler.addEventListener('hardwareBackPress', this._onBackPressed);
}
componentWillUnmount () {
BackHandler.removeEventListener('hardwareBackPress', this._onBackPressed);
}
_onBackPressed () {
console.log('backPress');
goBack(this.props.navigation);
return true;
}
onNext() {
this.props.navigation.navigate("Board", {content: ...});
}
}
Additional Info :
This BoardNavigator is the nested one of the rootNavigator(StackNavigator).
react : '16.0.0-alpha.12'
react-native : "0.47.2"
I've actually used backhandler like below for controlling back button to close the application just with 2 press immediately.
componentDidMount() {
this._backPress = 0;
BackHandler.addEventListener('backPress', () => {
setTimeout(() => {
this._backPress = 0;
}, 3000);
this._backPress += 1;
if (this._backPress <= 1) {
ToastAndroid.showWithGravity(strings.BACK_BUTTON_ALERT, ToastAndroid.SHORT, ToastAndroid.CENTER);
return true;
}
return false;
});
Related
I am currently transitioning from Wix RNN V1 to V2, and so far I've managed to find the appropriate replacement APIs, except for overriding the back button on Android.
In V1 we could pass the overrideBackPress: true attribute, and then handle back button presses manually on the cooresponding screen.
However, in V2 I've found no such replacement, and the only topics I could find were this thread:
https://github.com/wix/react-native-navigation/issues/4217
I've implemented the suggestions there, but Wix navigation is still automatically closing screens even though it should be overwritten.
Any known a solution for this?
I had the same issue and the only way i could override the backpress behavior on both platforms is to replace the left back button with custom button and use the BackHandler of react native for the hardware button in Android. The code is as below.
Component A
//Navigate to component B from A
Navigation.push(this.props.componentId, {
component: {
name: 'ComponentB',
options: {
topBar: {
leftButtons: [{
id: 'backPress',
text: 'Back',
icon: require('backbutton.png')
}]
},
}
}
});
Component B
import React, { PureComponent } from 'react';
import { View, BackHandler } from 'react-native';
import { Navigation } from 'react-native-navigation';
export default class ComponentB extends PureComponent {
constructor(props) {
super(props);
Navigation.events().bindComponent(this);
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackPress);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackPress);
}
navigationButtonPressed({ buttonId }) {
switch (buttonId) {
case 'backPress': {
this.handleBackPress();
break;
}
}
}
handleBackPress = () => {
//Custom logic
//Go back if required
Navigation.pop(this.props.componentId)
//Stop the default navigation
return true;
};
//Render component
render() {
return (<View></View>);
}
}
You can use registerScreenPoppedListener:
Navigation.events().registerScreenPoppedListener((event) => {
if (event.componentId === "my-screen-id") {
// do something
}
});
LoginScreen.js
this.props.navigator.push({
screen: "auxxa.LandingScreen",
passProps: { login: true },
overrideBackPress: true,
navigatorStyle: {
navBarHidden: true
}
});
LandingScreen.js
constructor(props) {
super(props);
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
// this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
this.state = {
size: { width, height },
tileData: null,
isLoading: true,
user_id: null,
refetching: false,
access_token: null
};
}
componentWillMount() {
BackHandler.addEventListener(
"hardwareBackPress",
this.handleBackButtonClick
);
}
handleBackButtonClick() {
console.log("check login " + this.props.login);
if (this.backPressed && this.backPressed > 0) {
if (this.props.login) {
console.log("login");
RNExitApp.exitApp();
} else {
console.log("root");
this.props.navigator.popToRoot({ animated: false });
return false;
}
}
this.backPressed = 1;
this.props.navigator.showSnackbar({
text: "Press one more time to exit",
duration: "long"
});
return true;
}
componentDidMount() {
BackHandler.removeEventListener(
"hardwareBackPress",
this.handleBackButtonClick
);
}
I used react-native-navigation from Wix for my app nevigation purpose.Here I have attached login screen and landing screen.after successful login app navigate to landing screen.after that I click back button It will return to login screen.I need to avoid that.How can I do that thing? I tried to exit from the app.But it also not working properly.
Please help me if some one know this.Thanks in advanced.
Use this call in handleBackButtonClick function and why are you removing the listener in componentDidMount ?
this.props.navigator.resetTo({ screen: 'example.ScreenThree'})
.
Uncomment this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this)); on your constructor to listen to navigation events
and add the navigationEvent listener method
onNavigatorEvent(event: NavigatorEvent) {
if (event.type === 'NavBarButtonPress') {
if (event.id === 'skill_information') {
// Add here whatever you would like to do (this.handleBackButtonClick() for example)
}
}
Being a newbie in RN programming, I'm trying to handle android hardware button. But pressing it on screen leads to simultaneously going to previous screen and closing app.
My StackNavigator looks like:
const navigatorApp = StackNavigator({
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
Screen3: { screen: Screen3 },
Screen4: { screen: Screen4 }
})
I tried to make a global backpress handling for screens like
class HandleHavigation extends React.Component {
componentWillMount () {
if (Platform.OS === 'android') return
BackHandler.addEventListener('hardwareBackPress', () => {
const { dispatch, nav } = this.props
if (nav.routes.length === 1 && (nav.routes[0].routeName === 'Screen1')) {
return false
}
dispatch({ type: 'Navigation/BACK' })
return true
})
}
componentWillUnmount () {
if (Platform.OS === 'android') return
BackHandler.removeEventListener('hardwareBackPress')
}
render () {
return <navigatorApp navigation={addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
addListener: createReduxBoundAddListener('root')
})} />
}
}
const mapStateToProps = state => ({ nav: state.reducer })
export default connect(mapStateToProps)(HandleNavigation)
I also tried some given in other questions solutions, but nothing helped to prevent app closing.
I also thought about realizing backHandler on every screen.
In my app every screen contains function onPress for top button. That is why I tried to copy this action to hardware button using Backhandler. But all I get - screen goes back, and the app hides at the same time.
Is there any solution in my case to prevent closing app by pressing hw backbutton?
You can use BackHandler to exit/close the application:
import { BackHandler } from 'react-native';
BackHandler.exitApp();
Use react-navigation it has inbuilt backhandler.
I have developed a PWA (Tab based) using Ionic 3. It is working fine until hardware back button or browser's back button is pressed in android browser. If it is running from home screen, pressing hardware back will close app. If app is running in chrome in android (only tested in chrome), hardware back or browser's back will reload PWA's first page, not previously visited page. How to handle these events in Ionic 3 PWA?
I am using lazy load for all pages.
What I tried so far:
As per jgw96's comment here, I thought IonicPage will handle navigation itself. But it is not working.
Used platform.registerBackButtonAction, but it's not for PWA.
As per Webruster's suggestion below in Answers, tried code in app.component.ts. But no change.
Posting code:
import { Component, ViewChild } from '#angular/core';
import { Nav, Platform, AlertController, Alert, Events, App, IonicApp, MenuController } from 'ionic-angular';
#Component({
templateUrl: 'app.html'
})
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage:any = 'TabsPage';
constructor(public platform: Platform,
public alertCtrl: AlertController, public events: Events,
public menu: MenuController,
private _app: App,
private _ionicApp: IonicApp) {
platform.ready().then(() => {
this.configureBkBtnprocess ();
});
}
configureBkBtnprocess() {
if (window.location.protocol !== "file:") {
window.onpopstate = (evt) => {
if (this.menu.isOpen()) {
this.menu.close ();
return;
}
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
return;
}
if (this._app.getRootNav().canGoBack())
this._app.getRootNav().pop();
};
this._app.viewDidEnter.subscribe((app) => {
history.pushState (null, null, "");
});
}
}
}
you have mentioned that you are working with the hardware back button on app and in browser so you didn't mention clearly what need to be done at what stage so i came up with the generalized solution which can be useful in most of the cases
app.component.ts
platform.ready().then(() => {
// your other plugins code...
this.configureBkBtnprocess ();
});
configureBkBtnprocess
private configureBkBtnprocess () {
// If you are on chrome (browser)
if (window.location.protocol !== "file:") {
// Register browser back button action and you can perform
// your own actions like as follows
window.onpopstate = (evt) => {
// Close menu if open
if (this._menu.isOpen()) {
this._menu.close ();
return;
}
// Close any active modals or overlays
let activePortal = this._ionicApp._loadingPortal.getActive() ||
this._ionicApp._modalPortal.getActive() ||
this._ionicApp._toastPortal.getActive() ||
this._ionicApp._overlayPortal.getActive();
if (activePortal) {
activePortal.dismiss();
return;
}
// Navigate back
if (this._app.getRootNav().canGoBack())
this._app.getRootNav().pop();
}
else{
// you are in the app
};
// Fake browser history on each view enter
this._app.viewDidEnter.subscribe((app) => {
history.pushState (null, null, "");
});
Solution 2
Try to Add the these event listener in the platform ready:
window.addEventListener('load', function() { window.history.pushState({}, '')
})
window.addEventListener('popstate', function() { window.history.pushState({},
'') })
I have pretty much same requirement but none of the solution completely works, so i came up with my own. here i have used an array to keep track of visited page and removes it on back click event.
Note: window.onpopstate gets called even on pushing new page
import { Platform, Nav } from "ionic-angular";
import { HomePage } from "../pages/home/home";
#Component({
templateUrl: "app.html"
})
export class MyApp {
rootPage: any;
#ViewChild(Nav) nav: Nav;
pageHistory: string[] = [];//to track page history
constructor(
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen
) {
window.addEventListener("load", function() {
//adding a state to prevent app exit on back
window.history.pushState({ noBackExitsApp: true }, "");
});
platform.ready().then(() => {
window.onpopstate = evt => {
let view = this.nav.getActive();
if (this.pageHistory.find(x => x === view.name)) {
if (!view.name.startsWith("Home")) {//handle a condition where you want to go back
this.pageHistory = this.pageHistory.filter(n => n !== view.name);
this.nav.pop().catch(reason => {
console.log("Unable to pop :" + reason);
});
}
} else {
window.history.pushState({ noBackExitsApp: true }, "");
this.pageHistory.push(view.name);
}
};
this.rootPage = HomePage;
statusBar.styleDefault();
splashScreen.hide();
});
}
}
After converting the app to redux, my react-navigation got some problem. Previously, before integrating with redux, when I press back button (Physical button) react-navigation back to the previous screen. After integrating with redux, the back button will close the app. But, it's still working with goBack() function.
I'm following the guide: https://reactnavigation.org/docs/guides/redux
And read some code from here : https://github.com/react-community/react-navigation/tree/master/examples/ReduxExample
And, this is my Navigator configuration
export const AppNavigator = StackNavigator(
{
Home: { screen: HomeScreen },
ChatDetail: { screen: ChatDetail },
PulsaDetail: { screen: PulsaDetailScreen },
Pulsa: { screen: Pulsa }
},
{
headerMode: 'none',
}
)
class AppWithNavigation extends Component {
render(){
return(
<AppNavigator navigation={ addNavigationHelpers({
dispatch: this.props.dispatch,
state: this.props.nav,
})} />
)
}
}
const mapStateToProps = (state) => ({
nav: state.nav
})
export default connect(mapStateToProps)(AppWithNavigation)
EDIT: It's can be done with manual handle & dispatch back action, but it's can't do it automaticlly? just like before using redux?
BackHandler.addEventListener('hardwareBackPress',() => {
this.props.goBack()
return true
})
After post Github issue in react-navigation repository, I got the answer.
Should add manually the back listener on top of screen / component
// App.js
import { BackAndroid } from 'react-native'
// [...]
componentDidMount() {
BackAndroid.addEventListener('backPress', () => {
const { dispatch, nav } = this.props
if (shouldCloseApp(nav)) return false
dispatch({ type: 'Back' })
return true
})
}
componentWillUnmount() {
BackAndroid.removeEventListener('backPress')
}
// [...]
https://github.com/react-community/react-navigation/issues/2117
https://github.com/react-community/react-navigation/issues/117
UPDATE:
https://facebook.github.io/react-native/docs/backhandler