Codementor Events

Ionic 3+ solving the hardware back button, avoiding to close the app

Published May 31, 2018Last updated Nov 27, 2018
Ionic 3+ solving the hardware back button, avoiding to close the app

Who doesn't? Believed in its own code at the point of dying for it... I am a mobile developer that always is facing challenges and looking for solutions.

The problem I wanted to solve

Think about, so you finished your project after months, or at least long hours working on that. And finally you show it up to the costumer. You prepared the flow, menu, tabs, everything to navigate in the app, and so on. But surprise! The first thing the user does is tap the hardware back button, "zzzumpt" your app desapear of the user eyes and some times the app crashes...

The hardware (headache) back button

Android devices have a built in "back" button. By default, when user presses the Android hardware back button, the navigation has to pop a screen or exit the app if there are no screens to pop. If you are using Ionic to develop, I might suggest you to take so much care about this.

To start we will use the Ionic Platform service. It can be used to get information about your current device through the platforms method, including whether the app is being viewed from a tablet, if it's on a mobile device or browser, and the exact platform (iOS, Android, etc). Also we will use the registerBackButtonAction() function associated to Platform method. And that's it, simple like that!

The process

In the app folder open up your app.component.ts file to start implementing the function:

platform.registerBackButtonAction(() => { 
}); 

This function accepts also one more parameter called priority, but we will not cover this part in this article, we can talk more about it a little bit if you ask me.

Considering that we are using lazy loading approach. Firstly let's import the App component and declare it in the constructor, as we will use it to manage and acquire the current view name.

import { App } from 'ionic-angular';
constructor(public  app: App) {

Your final code will be like that:

import { Nav, Platform, AlertController } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { Component, ViewChild } from '@angular/core';
import { StatusBar } from '@ionic-native/status-bar';
import { App } from 'ionic-angular';

@Component({
  templateUrl: 'app.html'
})

export class MyApp {
  @ViewChild(Nav) nav: Nav;

  rootPage: any = 'HomePage';

  pages: Array<{title: string, component: any}>;

  constructor(
    public platform: Platform, 
    public statusBar: StatusBar, 
    public splashScreen: SplashScreen,
    public alertCtrl: AlertController,
    public app: App,
  ){
    this.initializeApp();

    // used for an example of ngFor and navigation
    this.pages = [
      { title: 'Home', component: 'HomePage' },
      { title: 'Lista', component: 'ListPage' },
      { title: 'Sobre', component: 'SobrePage' },
      { title: 'Vazia', component: 'VaziaPage' },
    ];

  }

  initializeApp() {
    this.platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      this.statusBar.styleDefault();
      this.splashScreen.hide();
    });
    this.platform.registerBackButtonAction(() => {
      // Catches the active view
      let nav = this.app.getActiveNavs()[0];
      let activeView = nav.getActive();                
      // Checks if can go back before show up the alert
      if(activeView.name === 'HomePage') {
          if (nav.canGoBack()){
              nav.pop();
          } else {
              const alert = this.alertCtrl.create({
                  title: 'Fechar o App',
                  message: 'Você tem certeza?',
                  buttons: [{
                      text: 'Cancelar',
                      role: 'cancel',
                      handler: () => {
                        this.nav.setRoot('HomePage');
                        console.log('** Saída do App Cancelada! **');
                      }
                  },{
                      text: 'Fechar o App',
                      handler: () => {
                        this.logout();
                        this.platform.exitApp();
                      }
                  }]
              });
              alert.present();
          }
      }
  });
  }

  openPage(page) {
      // Reset the content nav to have just this page
      // we wouldn't want the back button to show in this scenario
      this.nav.setRoot(page.component);
  }   

  logout() {
      this.nav.setRoot('LoginPage');
  }

}

It was not difficult to solve this kind of situation using Ionic Framework document page, there are a lot of docs about it and the Forum to claim for help. Some times I am the one who helps others, and some times I am the one who ask for support also.

This tips is to grantee that your apps will not crash or at least not close at users faces. So I hope it cam be useful to increase the quality of your code and contribute to a better user experience.

Always take care about UX (user experience) in your apps and development projects. It can be a rouge difference between a simple good one app and a nice one with strong usability experience.

Feel free to keep in touch, or call me to a live. Hope to see you soon!

Discover and read more posts from Everton Costa
get started
post comments22Replies
john edgar otom
5 years ago

the code should have a ‘true/false’ condition to prevent multiple alertCtrl to appear

Everton Costa
5 years ago

Hi John!
Nice from you to share your ideas.
I am sure that after solving the main problem, it’s a good approach to put some flags to improve the usability and user experience.
Thanks!

Arumugam Nainar
6 years ago

nav.pop(); not working for me… i am using ionic-angular 3.9.2

Everton Costa
6 years ago

Do you imported the Nav correctly?
import { Nav, Platform, AlertController } from ‘ionic-angular’;
Have you declared as a view child?
@ViewChild(Nav) nav: Nav;

Arumugam Nainar
6 years ago

yeah… This my actual issue help me to solve this issue…

You can’t remove all the pages in the navigation stack. nav.pop() is probably called too many times

Unhandled Promise rejection: navigation stack needs at least one root page

Am using every page as module and import app.module.ts

app.component.ts

@ViewChild(Nav) nav: Nav;

rootPage: any = LoginPage;

// Catches the active view
let nav = this.app.getActiveNavs()[0];
let activeView = nav.getActive();
// Checks if can go back before show up the alert
if (activeView.name === ‘HomePage’) {
this.platform.exitApp();

  } else {
    nav.pop();
  }
kavitha gottipati
6 years ago

Is there any solution available in Ionic 4(4.1.1)?

Everton Costa
6 years ago

Hi Kavitha!
What is the error that you are getting?
So, I will check and return back.

kavitha gottipati
6 years ago

Hi Everton Costa,

Let me explain you clearly… I am using Ionic 4 with angular to build hybrid application and the scenario is, when user logIn to app and click back button(both browser/android hardware) from the home page, app should be closed instead of going back to login page (because user session is still valid in our case).

To restrict back button from Home page I have done below.

if (!this.isloggedIn()) {
console.log(‘user not logged In’);
// this.router.navigate([’/login’], { replaceUrl: true });
} else {
console.log(‘user logged In…Redirecting to Home’);
this.router.navigate([’/app-landing’], { replaceUrl: true });
}

The above code is just to restrict user by disabling back button from home page. But requirement is to keep back button as it is and let close the application as and when back button clicked.

I have tried below solution as per Ionic 4 but it is not working and not even firing alert statement.

import { Component } from ‘@angular/core’;
import { ToastController } from ‘@ionic/angular’;
import { Platform } from ‘@ionic/angular’;
import { SplashScreen } from ‘@ionic-native/splash-screen/ngx’;
import { StatusBar } from ‘@ionic-native/status-bar/ngx’;
import { Router } from ‘@angular/router’;

@Component({
selector: ‘app-root’,
templateUrl: ‘app.component.html’
})
export class AppComponent {
lastTimeBackPress = 0;
timePeriodToExit = 2000;

constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private router: Router,
private toastController: ToastController
) {
this.initializeApp();

}

initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
this.platform.backButton.subscribe( async() => {
if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
navigator[‘app’].exitApp();
} else if ( this.router.url === ‘/app-landing’ ) {
const toast = await this.toastController.create({
message: ‘Press back again to exit App’,
showCloseButton: false,
duration: 2000
});
toast.present();
this.lastTimeBackPress = new Date().getTime();
}
});

});

}
}

Please let me know if you have any solution for this problem…

Show more replies