Kivy. Build packages for Android and no magic

In yesterday’s Python article in Mobile development , which talked about the KivyMD library (a collection of Material Design-style widgets for use in the Kivy cross-platform framework), I was asked in the comments to talk about the process of building the package for the Android platform. For many, this process, unfortunately, was and remains something of a series of magical shamanism and not an affair for beginners. Well, let's figure it out, is everything really complicated and am I really a magician and wizard ...

Of course he could! So, you wrote your code in Python and Kivy. What does it take to run it on Android devices? Go to the KivyMD repository and you will see that these instructions have long spelled out the steps that will allow you to collect the APK package:

  1. Download XUbuntu 18.04

Install Virtual Box on your computer.
Create a new virtual machine based on a downloaded XUbuntu image
Start the XUbuntu virtual machine, open a terminal and run the following command:


 chmod +x 


All! You now have a virtual machine for building APK packages for Kivy apps! What's next? Let's actually build a test application. Create the TestKivyMD directory in the home directory of your virtual machine with the empty file:

Next, open the file and write the code for our test application that will use the KivyMD library:

 from kivy.lang import Builder from import MDApp KV = """ Screen: MDToolbar: title: "My firt app" elevation: 10 md_bg_color: app.theme_cls.primary_color left_action_items: [["menu", lambda x: x]] pos_hint: {"top": 1} MDRaisedButton: text: "Hello World" pos_hint: {"center_x": .5, "center_y": .5} """ class HelloWorld(MDApp): def build(self): return Builder.load_string(KV) HelloWorld().run() 

Save, open the terminal in the directory with the file and install the KivyMD library:

 sudo pip3 install kivymd 

After installation, you can test our code:


The result of the script will be a screen with the Toolbar and one button "Hello World":

Next, we need to create the buildozer.spec specification file, which should be located in the same directory as the file:

If you did not close the terminal (if the terminal was closed, open it in the TestKivyMD directory), enter the command:

 buildozer init 

This command will create a default specification file. Open it and edit:

 [app] # (str) Title of your application title = KivyMDTest # (str) Package name = kivymd_test # (str) Package domain (needed for android/ios packaging) package.domain = com.heattheatr # (str) Source code where the live source.dir = . # (list) Source files to include (let empty to include all the files) source.include_exts = py,png,jpg,jpeg,ttf # (list) Application version version = 0.0.1 # (list) Application requirements # comma separated eg requirements = sqlite3,kivy requirements = python3,kivy==1.11.1,kivymd # (str) Supported orientation (one of landscape, sensorLandscape, portrait or all) orientation = portrait # (bool) Indicate if the application should be fullscreen or not fullscreen = 1 # (list) Permissions android.permissions = INTERNET,WRITE_EXTERNAL_STORAGE # (int) Target Android API, should be as high as possible. android.api = 28 # (int) Minimum API your APK will support. android.minapi = 21 # (str) Android NDK version to use android.ndk = 17c # (bool) If True, then skip trying to update the Android sdk # This can be useful to avoid excess Internet downloads or save time # when an update is due and you just want to test/build your package android.skip_update = False # (bool) If True, then automatically accept SDK license # agreements. This is intended for automation only. If set to False, # the default, you will be shown the license when first running # buildozer. android.accept_sdk_license = True # (str) The Android arch to build for, choices: armeabi-v7a, arm64-v8a, x86, x86_64 android.arch = armeabi-v7a [buildozer] # (int) Log level (0 = error only, 1 = info, 2 = debug (with command output)) log_level = 2 # (int) Display warning if buildozer is run as root (0 = False, 1 = True) warn_on_root = 0 # (str) Path to build artifact storage, absolute or relative to spec file build_dir = ./.buildozer # (str) Path to build output (ie .apk, .ipa) storage bin_dir = ./bin 

Everything is clear here, therefore, additional comments are unnecessary. Read carefully the default specification, in it you can specify the path to the icon, flash when loading the application and much more. I left only what we now need to build our test package. And, actually, we start the build process with the command in the terminal:

 buildozer android debug 

You can safely go to the kitchen and make coffee, because for the first time the process of loading and compiling libraries will take a very long time. All subsequent assemblies take 10-20 seconds.

Coffee is drunk and it's time to look into the terminal:

Voila! Our application is built! It's time to drop it on your smartphone and run:

Everything works! And it turns out not everything is as complicated as it seemed.

They also asked me:

Neither Flutter nor React Native have any advantages over the Kivy Language markup language, which allows you to create and position live layouts and widgets. As for me, the way UI is built in Flutter is a real perversion. Only a sick person could think of this. In order not to be unfounded, let's look at the Flutter code and the Kivy code of the same simple application ... It will look like this:

Below I give the code from the article About Flutter, briefly: Basics :

 import 'package:flutter/widgets.dart'; main() => runApp( Directionality( textDirection: TextDirection.ltr, child: Container( color: Color(0xFFFFFFFF), child: App(), ), ), ); class App extends StatelessWidget { @override Widget build(BuildContext context) { return Center( child: GestureDetector( //     onTap: () { //    GestureDetector //    ,      print('You pressed me'); }, child: Container( //     decoration: BoxDecoration( //   shape:, //     color: Color(0xFF17A2B8), //      ), width: 80.0, height: 80.0, ), ), ); } } class Counter extends StatefulWidget { //      ,     , //   createState() @override State<Counter> createState() => _CounterState(); //        State, //   State<> } class _CounterState extends State<Counter> { //    -    , //      . //   ,     int counter = 0; //     ,       //   ,      Stateless . @override Widget build(BuildContext context) { //          , //     —  : return Center( child: GestureDetector( onTap: () { //  ,   ,    //  counter. setState(() { // setState()   ,    //      ,    ++counter; }); }, child: Container( decoration: BoxDecoration( shape:, color: Color(0xFF17A2B8), ), width: 80.0, child: Center( child: Text( //    counter '$counter', //      style: TextStyle(fontSize: 30.0), ), ), ), ), ); } } 

And here is exactly the same, but using Kivy and KivyMD:

 from kivy.lang import Builder from import MDApp KV = """ #:import get_color_from_hex kivy.utils.get_color_from_hex Screen: MDCard: MDLabel: value: 0 text: str(self.value) halign: "center" on_touch_down: self.value += 1 canvas.before: Color: rgba: get_color_from_hex("#4eaabe") Ellipse: pos:[0] - dp(25),[1] - dp(25) size: dp(50), dp(50) """ class HelloWorld(MDApp): def build(self): return Builder.load_string(KV) HelloWorld().run() 

In my opinion, the conclusion is obvious and does not need my comment ...

I hope it was useful to you. I’m leaving a poll on the topic “Did you manage to build an application for Android”.


All Articles