Semantics of the App Plugin using App.addListener("backButton", myCallback)

I am writing a mobile app for Android using Capacitor (v6) and Ionic (v8). I am also using the Vue.JS (v3) framework. Several of the views/pages in my app have some special handling for the hardware “back” button. So I’ve imported the @capacitor/App plugin to add a callback for the back button.

As the view/page is mounted, I call:

this.backListener = await App.addListener('backButton',this.execBack.bind(this))

When the page is unmounted I call:

this.backListener.remove()

Everything works. The callback is invoked as expected when the user taps the hardware back button.

The problem is that there is more than one page/view in my mobile app. For example, first view/page “A” is loaded and displayed. The back-button callback is registered as shown above when view/page “A” is mounted. Then the user taps a component which causes a new view/page “B” to open. This navigation is accomplished by using Vue’s router like this:

this.$router.push("/view-B")

So view/page “B” is now mounted (view/page “A” is still mounted too, but it is not visible). But this new page “B” has it’s own special handling for the “back” button, so page “B” also registers a back-button callback with the code shown above. So now both page “A” and page “B” have callbacks registered. When the user actually taps the hardware “back” button while on page “B”, both callbacks are invoked (one call to the callback registered for page A and another call to the callback registered for page B).

But I don’t want both callbacks to be called. Once page “B” is mounted, it makes no sense for the back-button callback from page “A” to execute any more, even though page “A” is still mounted and waiting to be reactivated when page “B” closes.

Of course, I can unregister the back button callback at any time with the code shown above. But that gets complicated quickly. I have to create a callback so that when page “A” loads page “B”, it unregisters the callback for page “A”. Then when page “B” is closed, page “A” automatically comes to the foreground again. I would need to detect that page “A” is currently being shown an re-register the back-button callback for page “A”. This seems like a nightmare to manage, especially as the pages get nested multiple levels deep. It feels like there should be a simpler way.

When each page has it’s own back-button callback, I want the only the callback for the active page to be invoked. Is there some simple way to implement this, or do I really need register and unregister these callbacks as navigation occurs with the Vue router?

Stepping back for a sec, what are you trying to accomplish when the user hits the back button? Maybe there is a better way of doing it.

If you absolutely need to use the backButton listener, I would probably just register it once when your app loads and then based on the URL, call different logic.

The the back button does different things, depending on which page is currently showing. For example, in the login page there is a 2-step process: first the user enters the username and presses “Next”, and then the password input shows up. If the user hits the hardware back button then it goes back to the username prompt. It’s just part of the requirements for the mobile app that I’m building. I don’t have the option of using something other than the hardware back button, because that is what is specified.

Having a single back-button callback registered globally when the app loads is going to be tricky. The callback code is going to have to somehow reach in to the Vue.JS components that are currently active and perform complex logic. Doing all that in a global callback means exposing all the inner workings of the various views/pages so they can be manipulated by a global callback. Yuck…

I see. If you don’t think there is another way, you could use Ionic’s Lifecycle Methods to either register/unregister the listener or just set an active flag and then within your logic, if not active, don’t run the code. Using the Lifecycle methods, Page A shouldn’t have to know about Page B and Page B shouldn’t have to know about Page A.