NativeScript - Capturing the Back Button on Android

Quite frequently you want to control what happens when your user hits the back button, well this is actually pretty simple to do on Android.  I've seen this question pop up several times; so it makes good blog post fodder.

You can either do this totally globally; or on a per page basis.   Globally, you just need to add the following code to your app.js file:

Global Event Handler:

// application variable should already be included in the app.js file
// Only do this on android
if (application.android) {
  application.android.on(application.AndroidApplication.activityBackPressedEvent, backEvent);
}

// This does the work on deciding if you want to go back
// arg.cancel = true will cancel navigating back
function backEvent(args) {
   if (dontGoBack) { args.cancel = true; }
}

You just need to figure out what your criteria is to handle canceling going back.

 

Individual Page Handler:

Now I personally prefer to put the handler on each page I need it on; so it is a little bit different.

You need to have a pageLoaded and a pageUnloaded event.

// Somewhere at the top
var application = require('application');

// Somewhere in your page you need to register your handler
exports.pageLoaded = function() {
    // We only want to register the event in Android
    if (application.android) {
        application.android.on(application.AndroidApplication.activityBackPressedEvent, backEvent);
    }
};

// When we navigate away from this page, we need to de-register the handler.
exports.pageUnloaded = function() {
    // We only want to un-register the event on Android
    if (application.android) {
        application.android.off(application.AndroidApplication.activityBackPressedEvent, backEvent);
    }
};

// This does your checks to see if you want to go back
// setting cancel=true will cancel the back 
function backEvent(args) {
  if (iRefuseToGoBack) { args.cancel = true; }
}

In your page.xml your "Page" declaration should look like:
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
      loaded="pageLoaded" unloaded="pageUnloaded">

You might notice in the individual page view version I register and de-register the event.  Each time the page is loaded it will register the event, if you don't de-register the event then that event handler will STILL run each time you hit the back button.  So it is very important if you are not using a global handler, to register on load, and de-register on unload.

 

Advanced Global Technique:

There is one other way to do this; I created this technique in my nativescript-orientation plugin and as I was finishing up this post I realized it could apply here and eliminates all the busy work of registering and de-registering handlers on each page but allows you per-page handlers.  I'm actually going to switch my apps to this way; because now that I wrote it -- it is my favorite way.  🙂

In your app.js file:

var frame = require('ui/frame');
if (application.android) {
    application.android.on(application.AndroidApplication.activityBackPressedEvent, backEvent);
}

function backEvent(args) {
    var currentPage = frame.topmost().currentPage;
    if (currentPage &amp;&amp; currentPage.exports &amp;&amp; typeof currentPage.exports.backEvent === "function") {
         currentPage.exports.backEvent(args);
   }   
}

Then in any page you want to control the back Event you do:
exports.backEvent = function(args) {
  if (iDontReallyWantToGoBack) { args.cancel = true; }
}

Pretty simple you only need to have a single global register; and it checks to see if the current page has a back handler which it will call if it exists.

Have fun, and now we can all stop going back.  😀

23 comments

      1. Hi Anderson,
        Angular way requires minimal changes. As module import and binding events are little different.

        Here is the angular way,
        import * as application from "application";

        ngAfterViewInit() {
        if (application.android) {
        application.android.on(application.AndroidApplication.activityBackPressedEvent, this.backEvent);
        }
        }

        ngOnDestroy() {
        // cleaning up references/listeners.
        if (application.android) {
        application.android.off(application.AndroidApplication.activityBackPressedEvent, this.backEvent);
        }
        }

        backEvent(args){
        if(this.isRefuseToGoBack){
        args.cancel = true;
        return;
        }
        }

        However i'm facing problem with the isRefuseToGoBack variable, it is always undefined in backEvent. Except this issue its working as expected.

        1. Awesome thanks for the code update of Angular Native. On your this.isRefuseToGoBack issue, did you create a isRefuseToGoBack variable inside your class?

        2. to bind the correct "this" use:

          backEvent = (args) => {
          if(this.isRefuseToGoBack){
          args.cancel = true;
          return;
          }
          }

    1. If you can duplicate this issue; I would recommend you put a new issue up on the NativeScript Github Issues so that it can be fixed in a future version...

  1. Here's what I've done to solve my issue...

    My app allows a user to sign in. Once they are in, my client did not want the back button to be used ONLY on the first screen after signing in. This would close the app and force them to re-sign in again. So I only prevented the back button on the first screen (after sign in).

    NOTE: I am using Angular in my NativeScript app.

    On the screen I wanted to control the back button, I added the following code to the constructor:

    this.page.on(Page.loadedEvent, event => {
    if (application.android) {
    application.android.on(application.AndroidApplication.activityBackPressedEvent, this.backEvent);
    }
    })

    this.page.on(Page.unloadedEvent, event => {
    if (application.android) {
    application.android.off(application.AndroidApplication.activityBackPressedEvent, this.backEvent);
    }
    })

    I then create a function after my ngOnInit function:

    private backEvent(args) {
    args.cancel = true;
    }

    That's it!

    NOTE: Don't forget to add the following before your @Component declaration:

    import { Page } from "ui/page";
    var application = require('application');

    Also, your constructor show have...
    constructor(private page: Page, ...

  2. Hi,

    Is the iOS version ready yet? We need this so badly, appreciate it if you have already finished the iOS version.

    1. The Nativescript-Master-technology plugin has a `exit` and `restart` function. So if you capture the back button, you can call the exit function.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.