Navigation with Back and Escape Buttons with Kivy on Android

As you write your app and decide to target the Android platform, you might have noticed that implementing navigation is a little harder than in native Java apps.
It’s harder, but not impossible.

There are different ways to do it, but it’s easiest when you use a ScreenManager to manage all your app’s screens.

ScreenManager

First, you need to add some code to catch the back button keypress so that Kivy doesn’t close the application right away. You may also want to catch the Escape button on computers, and the menu button on Android. Here’s how to do it.

Do not forget to add “import android” at the beginning of the script or you’ll get a NameError!

This assumes self.sm is your screen manager. If you named it differently, make sure you correct it. If you’re not using ScreenManager, skip this section, there’s a simpler implementation at the end.

Now you need to add the “on_{back,menu}_pressed” events to the Screens, otherwise you’ll get an error. Create a new object that inherits from Screen and add the new events:

That’s it! Now make sure every screen you use inherits from NormalScreen and override the “on_{back,menu]_pressed” methods; you should make sure they do one of the following actions:

    • Go back to the previous/home screen
    • Hide the keyboard (below)
    • Exit the application (below)
    • Close an open popup (use properties and edit the on_back_pressed method)

Managing history

Sometimes you want to write an application that can manage multiple dynamic screens. You can’t just keep all the screens in memory, especially if they contain images: it’s going to be sluggish and slow on low-end devices, and that’s not cool.

A solution for this is being able to re-create the screens. It’s simpler than it might seem.
This is the plan: when the user switches screen, the screen saves all the data it needs to replicate itself (class/screen name, content) and dumps it to memory, It’s going to be better than saving the Screen instance, because a simple list occupies less memory than a Screen with rendered textures and stuff. When the user presses the “back” button, the app iterates the history backwards and recreates the last screen.

Let’s translate this into code:

Now you can call App.get_running_app().previous() from the on_back_pressed method to go back to the previous screen. However you should always use “screen_props” instead of keyword arguments as those are the only ones saved in the history.
Note that this is a proof of concept and it hasn’t been tested. However there’s no reason why it shouldn’t work.

Hiding the keyboard

Android keyboard in Kivy apps is a pain in the neck, as sometimes it doesn’t close itself automatically, etc…

You can make sure that it’s gets closed before running this method by using this simple decorator (remember to import the “android” module!)

Put it in your App object, and apply it to any method you want. When they will be run, you will be sure there is no keyboard on top of the screen.

“Press back again to exit”

Another cool feature seen in Android apps is the “press back twice to exit” thing. It’s easy to implement and it’s also another way to hide the keyboard (you can make sure the keyboard is hidden on the first press without closing the app).

Popup/Modal/Anything else

Not every app is based on ScreenManager. One of mine has only one main screen and a few popups. In order to open Popups and close them, you can adapt this:

Now this may seem confusing but it’s really simple. When the user presses “back”, go_back is called. However go_back is a property and, while by default it does nothing (it’s a useless lambda), it’s overridden in __init__ and it points to go_back_default. BackPopup also has a go_back method which by default dismisses the popup and resets the app’s go_back to go_back_default. In order to take advantage of this we need to set the apps go_back to the Popup’s.

An example to clarify last paragraph:

I hope this has been useful to you.

There is a mobile optimized version of this page, view mobile Version.

11 Replies to “Navigation with Back and Escape Buttons with Kivy on Android”

    1. Yeah I had a hard time too, so I decided to write this article to help other people.
      I’m glad it helped you! If you want you can share it so it can help other people!

  1. Hey! That was helpful. I have a problem tough ( I am talking about “press back again to exit” ) when I type some text in textinput and then press the back button textinput’s text changes.
    For example, Let’s Say in textinput I typed “pizza”, now if I press the back key in my android device, keyboard hides but textinput displays “pizzapizza”, I wonder, what could cause that weird behaviour.

    1. Hi Memoize, I am not sure :/
      I think it’s some bug in Kivy though. Have you tried building it with an older/newer version of Kivy? I haven’t had that issue, that article is basically a collection of all the things I’ve done with the back button my apps, and I haven’t had any back button-related issue with any of them.
      You may want to ask the awesome people on IRC at #kivy on irc.freenode.net and link to my article and to a sample of code. I’m sure you’ll find some help there. I can’t really help you as I’m quite busy at this time.

      I’m glad you found it useful, though!

  2. Hi,
    I got a similar problem as Memoize, but not with the esc, but rather when starting an input, go to another screen and then go back to the first one (with the input). Then the text is doubled.
    Now back to your example (really well done!). How do you handle in that case the ESC functionality to defocus a textinput box (and thus also hide the keyboard)? This does not work anymore because goes always back to the previous screen. Any idea?

    1. Hello Filip, sorry for the delayed answer.
      For the first issue, can you send me a code sample that shows the issue? Just send me a link to a pastebin with the code.
      For the second issue, you can do this: save all your TextInputs/FocusBehaviors in a list. In _key_handler, when esc/back is pressed, iterate over that list, and if one of the items has attribute focus == True, simply return False. Returning True in an event handler basically means “this thing has been taken care of, don’t do anything else”, whereas False means it needs to be taken care of by some other handler (the default one, in this case).

  3. That is probably related to the code:
    if platform() == ‘android’:
    android.map_key(android.KEYCODE_BACK, 1001)

    It is not necessary anymore to bind this key and will only lead to more problems. In my case this was the not closing of the keyboard when changing screens and TextInput was active.
    Just remove:
    if platform() == ‘android’:
    android.map_key(android.KEYCODE_BACK, 1000)
    and probably this too:
    android.map_key(android.KEYCODE_MENU, 1001)
    (your BACK was 1000 and not 1001 Davide?)

    1. Thank you. I’ll test your suggestion and update the article.

      P.S. your comment got caught in the spam filter. I reported it as not spam, but make sure you check your email address/website as they might be in a blacklist.

  4. I’m new with Kivy I test this code using webview. I would know how I can create a back button because when I do return it nothing happens

    thank you

    WebView = autoclass(‘android.webkit.WebView’)
    WebViewClient = autoclass(‘android.webkit.WebViewClient’)
    activity = autoclass(‘org.renpy.android.PythonActivity’).mActivity

    class Wv(Widget):
    def init(self, kwargs):
    super(Wv, self).
    init(kwargs)
    Clock.schedule_once(self.create_webview, 0)

    class ServiceApp(App):

    if name == ‘main‘:
    ServiceApp().run()

    1. Hello Frederic,
      I’m currently unable to run your code (it crashes with no output on my device, for some reason). However I think the webview captures the button presses. For example, try to navigate, then press back. I think it will go up one history item.
      Try to ask at [email protected] how to make sure on_keyboard is handled by Kivy when a webview is shown above the app.

Leave a Reply