Ionic Capacitor Android Back Button Not Closing Alerts or Pickers

In my project, I've noticed that Android back button/gesture is not closing Alerts and Pickers before closing Modals, this is my fix.

Problem

On Android with Capacitor v3 in my project, I have a modal page with an ion-alert and an ion-datetime control on it. When I hit the system back button on my device, the picker interface doesn't close the overlays, instead closing the modal page. That's stupid, here's my fix.

Solution

1) Make your app.component.ts override the back button on Android

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AlertController, ModalController, NavController, PickerController, Platform } from '@ionic/angular';
import { App } from '@capacitor/app';


@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss'],
})
export class AppComponent {

  constructor(
    public router: Router,
    public _Platform: Platform,
    public _Nav: NavController,
    public _AlertController: AlertController,
    public _PickerController: PickerController,
    public _ModalController: ModalController
  ) {
    this.customBackButton();;
  }

  customBackButton() {
    App.addListener('backButton', () => {
      var homeUrl = '/home';
      var elWasDismissed: boolean = false;
      var a = new Promise<void>(resolve => {
        return this._AlertController.getTop().then(el => {          
          if (el) {
            elWasDismissed = true;
            el.dismiss().then(() => {
              resolve();
            });
          }
          else {
            resolve();
          }
        }); 
      });

      var b = a.then(() => {
        return new Promise<void>(resolve => {
          if (elWasDismissed) { 
            resolve(); 
          }
          else {
            this._PickerController.getTop().then(el => {          
              if (el) {
                elWasDismissed = true;
                el.dismiss().then(() => {
                  resolve();
                });
              }
              else {
                resolve();
              }
            }); 
          }
        });
      });

      var c = b.then(() => {
        return new Promise<void>(resolve => {
          if (elWasDismissed) { 
            resolve(); 
          }
          else {
            this._ModalController.getTop().then(el => {          
              if (el) {
                elWasDismissed = true;
                el.dismiss().then(() => {
                  resolve();
                });
              }
              else {
                resolve();
              }
            }); 
          }
        });
      });
      
      c.then(() => {
        if (!elWasDismissed) {
          if (this.router.url.indexOf(homeUrl) > -1) {
            App.exitApp();
          }
          else {
            this._Nav.navigateRoot(homeUrl);
          }
        }
      });
    });
  }
  
}

Note: On the above...

  1. list text hereI've harcoded my home-page url, you may or may not need this, up to you. I've done it this way so that when I'm on a different tab (I have 5), it first navigates back to the home tab. If you're on the home tab and you hit back, the app will exit. You can change this if you want.

  2. I know I can do this with the await/async syntax, but I don't like it. I prefer the promise long-hand syntax.

Conclusion

I've probably missed something out here, because there are components that present overlay style interfaces that I'm simply not using and have not accounted for. If you find one missing, comment here and I'll add it.

Hope this helps.

Comments: