Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App not exit when press back button on Android #2487

Closed
dreamteryst opened this issue Oct 10, 2017 · 10 comments
Closed

App not exit when press back button on Android #2487

dreamteryst opened this issue Oct 10, 2017 · 10 comments

Comments

@dreamteryst
Copy link

dreamteryst commented Oct 10, 2017

Version

  • react-native-router-flux v4.0.0-beta.21
  • react-native v0.49.0

Expected behaviour

  • Exit app when press back button.

This is my router.

<Router>
      <Scene key="root" hideNavBar>
        <Scene key="start" hideNavBar>
          <Scene key="splash" component={Splash} initial />
          <Scene key="startapp" component={AppStart} />
        </Scene>
        <Scene key="Drawer" drawer hideNavBar contentComponent={SideBar}>
          <Scene key="main" hideNavBar>
            <Scene key="home" component={Home} title="Main" initial />
            <Scene key="payment" component={Payment} title="Pricing" />
            <Scene key="vacuum" component={Vacuum} title="Vacuum" />
            <Scene key="iron" component={Iron} title="Iron" />
            <Scene key="dry" component={Dry} title="Dry" />
            <Scene key="history" component={History} title="History" />
            <Scene key="settings" component={Settings} title="Setiing" />
            <Scene key="notipay" component={Notipay} title="Notipay" />
            <Scene key="usernoti" component={UserNoti} title="UserNoti" />
          </Scene>
        </Scene>
        <Scene key='EmpDrawer' drawer hideNavBar contentComponent={EmpSideBar}>
          <Scene key="EmpMain" hideNavBar>
            <Scene key="empoyee" component={Employee} title="Employee" initial />
            <Scene key="settings" component={Settings} title="Setiing" />
          </Scene>
        </Scene>
        <Scene key="auth" hideNavBar>
          <Scene key="welcome" component={Welcome} title="Welcome To WashClean" initial />
          <Scene key="login" component={Login} title="Please Login" />
          <Scene key="register" component={Register} title="Register" />
          <Scene key="privacy" component={Privacy} title="Privacy" />
          <Scene key="terms" component={Terms} title="Terms" />
        </Scene>
      </Scene>
    </Router>

Go to Drawer or auth :

Actions.Drawer({type: ActionConst.RESET});
Actions.auth({type: ActionConst.RESET});

Handle back button :

componentDidMount () {
    BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
  }

  componentWillUnmount () {
    BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
  }

  onBackPress () {
    if (Actions.state.index === 0) {
      return false;
    }

    Actions.pop();
    return true;
  }

Actual behaviour

onBackPress return false but my app nothing happen.

@vishnuc
Copy link

vishnuc commented Oct 11, 2017

same thing happens to me

@mpochron
Copy link

+1

@dreamteryst
Copy link
Author

I found solution, just use BackHandler.exitApp() instead of return false
My code :

constructor(props) {
    super(props);
    this.state = {
      doubleBackToExitPressedOnce: false
    }
  }

  componentDidMount() {
    BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
  }

  onButtonPress = () => {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    // then navigate
    // navigate('NewScreen');
  }

  handleBackButton = () => {
    if(this.state.doubleBackToExitPressedOnce) {
      BackHandler.exitApp();
    }
    ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
    this.setState({ doubleBackToExitPressedOnce: true });
    setTimeout(() => {
      this.setState({ doubleBackToExitPressedOnce: false });
    }, 2000);
    return true;
  }

@vishnuc
Copy link

vishnuc commented Oct 21, 2017

Actual solution is to use backAndroidHandler in <Router> and return false only in home scene to exit.. you can get current scene by Actions.currentScene

@GenericJam
Copy link

GenericJam commented Dec 14, 2017

As a workaround, you can get a hold of the action and test for 'Navigation/BACK' in the reducer. Then you can call BackHandler.exitApp(). Obviously, this solution is sub optimal but it does work if you're stuck as the listener doesn't fire but the events are still coming through the reducer.
Here is the reducer from the sample project amended for this purpose:

const reducerCreate = params => {
  const defaultReducer = new Reducer(params);
  return (state, action) => {
    if(action.type === 'Navigation/BACK' && state.index === 0){
        BackHandler.exitApp()
    }
    console.log('ACTION:', action);
    return defaultReducer(state, action);
  };
};

Officially, I don't recommend this but I didn't find any other way to make it happen.

@Blapi
Copy link
Collaborator

Blapi commented Dec 15, 2017

as @vishnuc said, add this in your Router code :

  onBackPress() {
    if (Actions.state.index === 0) {
      return false
    }
    Actions.pop()
    return true
  }
...
...
  <Router backAndroidHandler={this.onBackPress}>

Then, the backButton will be handled by the library and if you want to do a specific action in a component with the backButton, it will override the onBackPress function.

Like this :

import {  BackHandler} from 'react-native'
...
...
  componentWill/DidMount() {
      this.homeBackPressHandler = BackHandler.addEventListener('homeBackPress', () => {
        if (Actions.currentScene === 'home' && myCondition) {
          doStuff()
          return true
        }
        return false
      })
  }

  componentWillUnmount() {
    this.homeBackPressHandler.remove()
  }

Doing that in my Android app, never had a problem.

@Blapi Blapi closed this as completed Dec 15, 2017
@vikrantnegi
Copy link

I'm having an issue with exiting the app when navigation from other screens work on initial load though.
https://stackoverflow.com/questions/47898965/exist-app-when-clicked-back-on-home-screen

@abhishekgargx
Copy link

Hope this will help : https://gist.github.com/Abhishekgarg727/447727a32e284c24346ec6d1a83feefe

@nwaughachukwuma
Copy link

I found solution, just use BackHandler.exitApp() instead of return false
My code :

constructor(props) {
    super(props);
    this.state = {
      doubleBackToExitPressedOnce: false
    }
  }

  componentDidMount() {
    BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
  }

  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
  }

  onButtonPress = () => {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    // then navigate
    // navigate('NewScreen');
  }

  handleBackButton = () => {
    if(this.state.doubleBackToExitPressedOnce) {
      BackHandler.exitApp();
    }
    ToastAndroid.show('Press back again to exit', ToastAndroid.SHORT);
    this.setState({ doubleBackToExitPressedOnce: true });
    setTimeout(() => {
      this.setState({ doubleBackToExitPressedOnce: false });
    }, 2000);
    return true;
  }

@dreamteryst how can this be made to work when there's an app drawer? My challenge is that subsequent screens continue to see Home screen, as this.props.navigation.state.routeName === 'Home'.

@lordenas
Copy link

@dreamteryst Blapi

as @vishnuc said, add this in your Router code :

  onBackPress() {
    if (Actions.state.index === 0) {
      return false
    }
    Actions.pop()
    return true
  }
...
...
  <Router backAndroidHandler={this.onBackPress}>

Then, the backButton will be handled by the library and if you want to do a specific action in a component with the backButton, it will override the onBackPress function.

Like this :

import {  BackHandler} from 'react-native'
...
...
  componentWill/DidMount() {
      this.homeBackPressHandler = BackHandler.addEventListener('homeBackPress', () => {
        if (Actions.currentScene === 'home' && myCondition) {
          doStuff()
          return true
        }
        return false
      })
  }

  componentWillUnmount() {
    this.homeBackPressHandler.remove()
  }

Doing that in my Android app, never had a problem.

aaaa. thank you! I half a day could not find a solution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants