Kivy. Сборка пакетов под Android и никакой магии


    Во вчерашней статье Python в Mobile development, в которой речь шла о библиотеке KivyMD (коллекции виджетов в стиле Material Design для использования их в кроссплатформенном фреймворке Kivy), в комментариях меня попросили рассказать о процессе сборки пакета для платформы Android. Для многих этот процесс, к сожалению, был и остается чем-то из ряда магического шаманства и не подъёмным для новичков делом. Что ж, давайте разбираться, так ли на самом деле все сложно и действительно ли я маг и волшебник...



    Конечно, мог бы! Итак, вы написали свой код на Python и Kivy. Что нужно для того, чтобы это можно было запустить на Android устройствах? Перейдите в репозиторий KivyMD и вы увидите, что в этой инструкции уже давно прописаны шаги, которые позволят вам собрать APK пакет:

    1. Загрузите XUbuntu 18.04

    Установите Virtual Box на свой компьютер.
    Создайте новую виртуальную машину на основе загруженного образа XUbuntu
    Запустите виртуальную машину XUbuntu, откройте терминал и выполните нижеследующую команду:

    wget https://github.com/HeaTTheatR/KivyMD-data/raw/master/install-kivy-buildozer-dependencies.sh

    chmod +x install-kivy-buildozer-dependencies.sh

    ./install-kivy-buildozer-dependencies.sh

    Все! Теперь у вас есть виртуальная машина для сборки APK пакетов для приложений Kivy! Что дальше? Давайте, собственно, займемся сборкой тестового приложения. Создайте в домашнем каталоге вашей виртуальной машины директорию TestKivyMD с пустым файлом main.py:


    Далее откройте файл main.py и напишите код нашего тестового приложения, которое будет использовать библиотеку KivyMD:

    from kivy.lang import Builder
    
    from kivymd.app 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()
    

    Сохраните, откройте терминал в директории с файлом main.py и установите библиотеку KivyMD:

    
    sudo pip3 install kivymd
    

    После установки можно протестировать наш код:

    
    python3 main.py
    

    Результатом работы скрипта будет экран с Toolbar и одной кнопкой «Hello World»:


    Дальше нам нужно создать файл спецификации buildozer.spec, который должен располагаться в той же директории, что и файл main.py:


    Если вы не закрывали терминал (если терминал был закрыт, откройте его в директории TestKivyMD), введите команду:

    
    buildozer init
    

    Эта команда создаст дефолтный файл спецификации. Откройте его и отредактируйте:

    
    [app]
    
    # (str) Title of your application
    title = KivyMDTest
    
    # (str) Package name
    package.name = kivymd_test
    
    # (str) Package domain (needed for android/ios packaging)
    package.domain = com.heattheatr
    
    # (str) Source code where the main.py 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 e.g. 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 (i.e. .apk, .ipa) storage
    bin_dir = ./bin
    

    Здесь все понятно поэтому дополнительные комментарии излишни. Почитайте внимательно дефолтную спецификацию, в ней можно указать путь к иконке, пресплеш при загрузке приложения и многое другое. Я оставил лишь то, что нам сейчас нужно для сборки нашего тестового пакета. И, собственно, запускаем процесс сборки командой в терминале:

    
    buildozer android debug
    

    Можете смело идти на кухню и заваривать кофе, потому что в первый раз процесс загрузки и компиляции библиотек займет очень много времени. Все последующие сборки проходят за 10-20 секунд.

    Кофе выпит и самое время заглянуть в терминал:


    Вуаля! Наше приложение построено! Самое время закинуть его на смартфон и запустить:


    Все работает! И оказывается не все так сложно, как казалось.

    Также меня спрашивали:


    Ни у Flutter ни у React Native нет преимуществ перед языком разметки Kivy Language, которая позволяет создавать и позиционировать лайоуты и виджеты. Как по мне, то, как строится UI во Flutter — это самое настоящее извращение. Придумать это мог только больной на голову человек. Чтобы не быть голословным, давайте посмотрим на код Flutter и код Kivy одного и того же простейшего приложения… Выглядеть оно будет следующим образом:


    Ниже я привожу код из статьи Про Flutter, кратко: Основы:

    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: BoxShape.circle, // зададим ему круглую форму
                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: BoxShape.circle,
                color: Color(0xFF17A2B8),
              ),
              width: 80.0,
              child: Center(
                child: Text( // выводим значение свойства counter
                  '$counter', // чтобы следить за его изменением
                  style: TextStyle(fontSize: 30.0),
                ),
              ),
            ),
          ),
        );
      }
    }
    

    А вот абсолютно тоже самое, но с использованием Kivy и KivyMD:

    from kivy.lang import Builder
    from kivymd.app 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: self.center[0] - dp(25), self.center[1] - dp(25)
                    size: dp(50), dp(50)
    """
    
    class HelloWorld(MDApp):
        def build(self):
            return Builder.load_string(KV)
    
    HelloWorld().run()
    

    По-моему, вывод очевиден и не нуждается в моем комментировании…

    Надеюсь, был вам полезен. Оставляю опрос на тему «Удалось ли вам построить приложение для Андроид».

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Удалось ли вам построить приложение для Андроид по этой инструкции?

    • 75,9%Да22
    • 24,1%Нет7
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 113

      +3
      Как по мне, то, как строится UI во Flutter — это самое настоящее извращение. Придумать это мог только больной на голову человек.

      Видимо, я еще более больной на голову человек, потому что считаю, что система построения UI во флаттере – это едва ли не лучшее решение на рынке: статическая типизация, автодополнения от IDE, все фишки языка + не надо отдельно изучать язык разметки, composition over inheritance. Ну и примеры для флаттера и для киви у Вас все-таки разные.

        0

        Абсолютно идентичные!

          +3

          Даже если не принимать во внимание такие вещи, как размер и цвет шрифта (которые Вы не указываете в примере с киви), код на флаттере можно безболезненно сократить, например, до такого:


          import 'package:flutter/material.dart';
          import 'package:flutter/widgets.dart';
          
          main() => runApp(MaterialApp(home: Scaffold(body: Counter())));
          
          class Counter extends StatefulWidget {
            @override
            State<Counter> createState() => _CounterState();
          }
          
          class _CounterState extends State<Counter> {
            int _counter = 0;
          
            @override
            Widget build(BuildContext context) => Center(
                  child: GestureDetector(
                    onTap: () => setState(() => _counter++),
                    child: Container(
                      decoration: BoxDecoration(
                        shape: BoxShape.circle,
                        color: Color(0xFF4eaabe),
                      ),
                      width: 80.0,
                      child: Center(
                        child: Text(
                          _counter.toString(),
                          style: TextStyle(fontSize: 30.0, color: Colors.white),
                        ),
                      ),
                    ),
                  ),
                );
          }
            +1

            Мое мнение останется неизменным в любом случае — это невозможно читать!

              +3

              Ок. Вот только подписывать всех людей с мнением, отличным от Вашего, под больных – так себе занятие.

                –3

                Это не только мое мнение. Код на Flutter абсолютно не читаем. Умные люди не зря придумали DSL языки разметки UI, а пихать все в код — это удел не совсем умных людей.

              0

              Ну, и да — указал размер шрифта:


              from kivy.lang import Builder
              from kivymd.app 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
                      font_size: "30sp"
              
                      canvas.before:
                          Color:
                              rgba: get_color_from_hex("#4eaabe")
                          Ellipse:
                              pos: self.center[0] - dp(25), self.center[1] - dp(25)
                              size: dp(50), dp(50)
              """
              
              class HelloWorld(MDApp):
                  def build(self):
                      return Builder.load_string(KV)
              
              HelloWorld().run()
                0
                pos: self.center[0] — dp(25), self.center[1] — dp(25)

                Фу, это не про это случайно была статья на хабре про "ошибка в проектировании за которую мне стыдно"


                напоминает мне как я был в первом классе и рисовал в "Лого"

                0

                глянул на это дело и запахло лиспом :)

                  +3

                  Если от скобочек начинает рябить в глазах, то это хороший показатель, что надо выносить код в отдельные виджеты/функции. Этим и хороши разметка в коде и общий принцип composition over inheritance во флаттере – вы просто применяете те же самые принципы, что и в остальном коде. Например (утрированный пример, но сам принцип такой):


                  Код
                  import 'package:flutter/material.dart';
                  import 'package:flutter/widgets.dart';
                  
                  main() => runApp(MaterialApp(home: Scaffold(body: Counter())));
                  
                  class Counter extends StatefulWidget {
                    @override
                    State<Counter> createState() => _CounterState();
                  }
                  
                  class _CounterState extends State<Counter> {
                    int _counter = 0;
                  
                    @override
                    Widget build(BuildContext context) => Button(
                          onTap: () => setState(() => _counter++),
                          child: ButtonText(text: _counter.toString()),
                        );
                  }
                  
                  class Button extends StatelessWidget {
                    const Button({Key key, this.onTap, this.child}) : super(key: key);
                  
                    final VoidCallback onTap;
                    final Widget child;
                  
                    @override
                    Widget build(BuildContext context) => GestureDetector(
                          onTap: onTap,
                          child: Container(
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              color: Color(0xFF4eaabe),
                            ),
                            width: 80.0,
                            child: child,
                          ),
                        );
                  }
                  
                  class ButtonText extends StatelessWidget {
                    const ButtonText({Key key, this.text}) : super(key: key);
                  
                    final String text;
                  
                    @override
                    Widget build(BuildContext context) => Center(
                          child: Text(
                            text,
                            style: TextStyle(fontSize: 30.0, color: Colors.white),
                          ),
                        );
                  }

                  Button и ButtonText при этом можно вынести в отдельные файлы, и вот у вас готовы переиспользуемые компоненты. А можно их просто сделать внутренней функцией, как в моем примере ниже. Такой гибкости очень сложно добиться в подходе с отдельным языком для разметки (либо сам этот язык становится слишком сложным, и возникает вопрос – а зачем писать приложение сразу на 2 языках программирования). И много компаний идут именно в этом направлении – iOS и SwiftUI, Android и Jetpack Compose (и что-то похожее было до этого у JetBrains с Anko Layout), тот же React и JSX.

                    0
                    тот же React и JSX

                    JSX это всё-таки смесь ежа с ужом кода и разметки, в отличии от Flutter, где только код.
                      0

                      Ну как сказать – это ж просто синтаксический сахар, чтобы оно выглядело похоже на html: It is called JSX, and it is a syntax extension to JavaScript.

                      0

                      В целом хорошо, если было бы {} вместо () было бы вообще привычно почти как какой ни будь жсон. У меня когда то давно первое время после знакомства с питоном были проблемы с возвращением на языки с си подобными скобочками. Со временем понимаешь что это все не важно. К тому же без подсветки синтаксиса ваш пример не так хорошо смотрится как мог бы. К тому же есть и плагины для разноцветных скобочек, и для отображения древовидной структуры. Лучше скринте свое IDE.

              +1

              Хотелось бы увидеть приложение и для ios так как и прошлая статья направлена на андроид… Попробуйте собрать приложение под ios на flutter и на kivy…

                0

                Да без проблем!


                image

                  0

                  Скрины можно? То что это скомпилировалось это уже хорошо. Но как при этом будет выглядеть приложение на ios?

                    +2

                    Я не занимаюсь этим по одной простой причине — у меня нет iOS. А гонять все это в эмуляторе в X-Code смысла не вижу.

                      +1
                      Но как при этом будет выглядеть приложение на ios?

                      Точно так же как на Андроид, если вы юзаете KivyMD.

                        0

                        Проблема именно в этом нативные виджеты android и ios визуально отличаются и flutter очень хорошо это сглаживает...

                  +1

                  Также хотелось бы увидеть более живой пример, например с отображением геолокации на карте на флаттер и на киви. Вот это был бы реальный пример...

                    0
                    Правильно я понял, что и не flexbox-контейнеры и не grid, а что то своё? Есть ли пересечение в подходе с общепринятыми методами позиционирования?

                    Как будет выглядеть описание «типичного» элемента списка в KivyMD?
                    image
                      0

                      То есть, привести разметку данной карточки?

                        0
                        Да, если не трудно. Можно даже не всей, а например «шапки» — ширина 100%, внутри два блока прижатые к краям, левый из двух горизонтальных элементов, правый из двух вертикальных. Без стилей, просто разметка.
                      0

                      Это, как вы выразились, не "типичный" элемент в KivyMD! Но, хорошо, я покажу...

                        0
                        Имеется в виду «типичный» не применительно к конкретному фреймворку, а к реальному приложению, такие элементы списка довольно часто встречаются в мобильных приложениях.
                          0

                          У нас нет набора "типичных" карточек. Они все разные и индивидуальные для любого приложения. У нас есть базовый класс MDCard и набор классов MDCardPost:


                          image


                          Конкретно такой карты, как привели вы — нет.

                            0

                            image

                            +1
                            Если брать Flutter, то такая карточка довольно просто описывается (схематично):

                            Column(
                              children: [
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                  children: [
                                    Row(
                                      children: [
                                        Icon,
                                        Text
                                      ]
                                    ),
                                    Column(
                                      crossAxisAlignment: CrossAxisAlignment.end,
                                      children: [
                                        Text,
                                        Text
                                      ]
                                    ),
                                  ]
                                ),
                                Text,
                                Text,
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                  children: [
                                    Row(
                                      children: [ Chip, Chip, Chip ]
                                    ),
                                    Row(
                                      children: [
                                        Icon,
                                        Text
                                      ]
                                    ),
                                  ]
                                ),
                              ]
                            )

                              0

                              Да чего уж там, приведите полный пример этой карты. Я в свою очередь сейчас размечу данную карту...

                                0
                                Вы в том плане, что он будет плохо читаем? Я не буду спорить, код будет многострочный, от количества закрывающих скобочек будет кружиться голова. Но есть декомпозиция и т.п.

                                Но не суть, я не про красоту, простоту и читаемость, в данный момент мне интересно, как на KivyMD сделать разметку с главной и поперечной осями.
                                  0

                                  Готово! Так пойдет или стили меток выдержать?


                                  image

                                    0
                                    Ох, ёшки-матрёшки, я ж вас не на слабо беру, типа «так не получится». Но за то что сделали, респект.
                                    Просто покажите, как выглядит код разметки, а я буду смотреть и думать, сложно-не-сложно для меня, сравнивать, думать о том, стоит ли возвращаться к Kivy, в своё время совсем не зашёл, может появление KivyMD всё изменило.
                                      +1

                                      image


                                      from kivy.lang import Builder
                                      from kivymd.app import MDApp
                                      
                                      KV = """
                                      <MDLabel>:
                                          size_hint_y: None
                                          height: self.texture_size[1]
                                          pos_hint: {"center_y": .5}
                                      
                                      <MDChip>:
                                          pos_hint: {"center_y": .5} 
                                      
                                      Screen:
                                      
                                          MDCard:
                                              orientation: "vertical"
                                              padding: "5dp"
                                              spacing: "10dp"
                                              size_hint_y: None
                                              height: self.minimum_height
                                              pos_hint: {"center_y": .5}
                                      
                                              BoxLayout:
                                                  size_hint_y: None
                                                  height: self.minimum_height
                                                  spacing: "10dp"
                                                  padding: "10dp"
                                      
                                                  Image:
                                                      size_hint: None, None
                                                      size: "40dp", "40dp"
                                                      source: "data/logo/kivy-icon-128.png"
                                      
                                                  MDLabel:
                                                      text: "User 123"
                                                      font_style: "H6" 
                                                      bold: True               
                                      
                                                  Widget:
                                      
                                                  BoxLayout:
                                                      orientation: "vertical"
                                                      spacing: "5dp"
                                      
                                                      MDLabel:
                                                          text: "2019-12.08 20:55"
                                                          font_style: "Button"
                                                          halign: "right"
                                      
                                                      MDLabel:
                                                          text: "Development"
                                                          font_style: "H6"
                                                          halign: "right"
                                                          bold: True 
                                      
                                              MDLabel:
                                                  text: "Как перенести Linux из embedded"
                                                  font_style: "H4"
                                                  bold: True 
                                      
                                              MDLabel:
                                                  text:
                                                      "Да, если не трудно. Можно даже не всей, а например «шапки» — " \
                                                      "ширина 100%, внутри два блока прижатые к краям, левый из " \
                                                      "двух горизонтальных элементов, правый из двух вертикальных. " \
                                                      "Без стилей, просто разметка."
                                      
                                              BoxLayout:
                                                  size_hint_y: None
                                                  height: self.minimum_height
                                                  spacing: "5dp"
                                      
                                                  MDChip:
                                                      label: "arm"
                                                      icon: ''
                                      
                                                  MDChip:
                                                      label: "linux"
                                                      icon: ''
                                      
                                                  MDChip:
                                                      label: "qume"
                                                      icon: ''
                                      
                                                  Widget:
                                      
                                                  BoxLayout:
                                                      size_hint: None, None
                                                      size: self.minimum_size
                                                      padding: "10dp"
                                      
                                                      MDIconButton:
                                                          user_font_size: "18sp"
                                                          icon: "mail"
                                      
                                                      Label:
                                                          text: "13"
                                                          color: 0, 0, 0, 1
                                                          size_hint: None, None
                                                          size: self.texture_size
                                                          pos_hint: {"center_y": .5}
                                      """
                                      
                                      class Card(MDApp):
                                          def build(self):
                                              return Builder.load_string(KV)
                                      
                                      Card().run()
                                        0
                                        Спасибо.
                                          +3
                                          Вот посмотрел я на ваш код, скажите пожалуйста, как обстоят дела с автокомплитом и поддержкой IDE? По сути это все один большой комментарий для меня на данный момент, было бы логичней делать что-то вроде шаблонов в Django/Jinja2 где файл шаблона лежит отдельно и наполняется контекстом. Сильно не пинайте, если говорю глупости. Просто как это рефакторить в случае чего, я не представляю.
                                            0

                                            Для pycharm есть плагин для подсветки синтаксиса kv

                                              0

                                              kv-файлы и должны лежать отдельным файлом.

                                                +1
                                                В VScode есть плагин для kv файлов, в которые можно разметку вынести.
                                    0

                                    Только самодостаточный: скомпилировал — запустил!

                                      0
                                      Как вариант:
                                      import 'package:flutter/material.dart';
                                      import 'package:flutter/widgets.dart';
                                      
                                      main() => runApp(MaterialApp(home: Scaffold(body: MyCard())));
                                      
                                      class MyCard extends StatelessWidget {
                                        @override
                                        Widget build(BuildContext context) => Center(
                                              child: Card(
                                                child: Padding(
                                                  padding: const EdgeInsets.all(8),
                                                  child: Column(
                                                    mainAxisSize: MainAxisSize.min,
                                                    crossAxisAlignment: CrossAxisAlignment.start,
                                                    children: <Widget>[
                                                      _buildHeader(),
                                                      _buildTitle(context),
                                                      _buildContent(),
                                                      _buildFooter(),
                                                    ],
                                                  ),
                                                ),
                                              ),
                                            );
                                      
                                        Widget _buildHeader() => Row(
                                              children: <Widget>[
                                                Icon(Icons.person, size: 48),
                                                Expanded(child: Text('User 123')),
                                                Column(
                                                  crossAxisAlignment: CrossAxisAlignment.end,
                                                  children: <Widget>[
                                                    Text('2019-12-08 20:55'),
                                                    Text('User 123'),
                                                  ],
                                                ),
                                              ],
                                            );
                                      
                                        Widget _buildTitle(BuildContext context) => Padding(
                                              padding: const EdgeInsets.symmetric(vertical: 8),
                                              child: Text(
                                                'Как перенести Linux из embedded',
                                                style: Theme.of(context).textTheme.title,
                                              ),
                                            );
                                      
                                        Widget _buildContent() => Padding(
                                              padding: const EdgeInsets.only(bottom: 16),
                                              child: Text(text),
                                            );
                                      
                                        Widget _buildFooter() => Row(
                                              children: <Widget>[
                                                Expanded(
                                                  child: Wrap(
                                                    spacing: 5,
                                                    children: <Widget>[
                                                      Chip(label: Text('arm')),
                                                      Chip(label: Text('linux')),
                                                      Chip(label: Text('qemu')),
                                                    ],
                                                  ),
                                                ),
                                                Icon(Icons.comment),
                                                Text('13'),
                                              ],
                                            );
                                      
                                        String get text =>
                                            'Да, если не трудно. Можно даже не всей, а например «шапки» — '
                                            'ширина 100%, внутри два блока прижатые к краям, левый из двух '
                                            'горизонтальных элементов, правый из двух вертикальных. '
                                            'Без стилей, просто разметка.';
                                      }
                                        –3

                                        Это просто вырвиглазной пи… ец!

                                          0

                                          Это очень аргументированное мнение!

                                            0

                                            Даже у Java все логично построено в их xml-разметке и спорить с этим бесполезно, у Flutter просто ужасный код!

                                              0
                                              С релизом Jetpack Compose там будет так же, как и на флаттере
                                                –2

                                                В Java взрослые люди стараются писать UI декларативно, а не заниматься извращегиями с XML.

                                              0
                                              А можно более развернутую аргументацию? Я не особо силен в питоне и код на флаттере для меня значительно читаемее выглядит.
                                                –2

                                                Даже те, кто используют Flutter, согласны, что код этого фреймворка "паравозный" и выгрвиглазной. Но вы, видимо, очередной троллейбус, так что ваше сообщение остается без ответа!

                                                  +3
                                                  С вашей точки зрения существует только два мнения — ваше и не правильное?
                                      0
                                      Думаю будет полезно добавить в статью последовательность сборки с использованием Docker. Проще потом автоматизировать сборку, да и целую виртуалку тащить на линуксовый хост как-то не очень имхо
                                        0

                                        Я не работал с Docker да и времени, если честно, в этом разбираться как бы нету.

                                        0
                                        1) Пример на Flutter не работает :/
                                        2) Если смотреть по коду, то не особо то и больше получается на Flutter (Если не считать строки со скобками)
                                        3) Давайте этот же пример, но вместо счётчика будем рандомить цвет круга и менять его с анимацией длительностью 1 секунда и кривой easeOut (Мне интересно, как обстоят дела с анимацией)
                                          0
                                          Выглядит красиво и надо будет поковырять это самостоятельно. Несколько предложений на будущее:
                                          — разметку вынести в отдельные файлы. В этом случае можно уже создать плагины для IDE, которые бы подсвечивали синтаксис разметки, позволяли автокомплит и рефакторинг и т.п. Ну и в целом удобнее, в этом случае версткой и кодом могли бы заниматься разные люди.
                                          — создать докер-образ для сборки под андроид. Тогда действительно можно будет билдить одной командой, не ставя зависимостей.

                                          Кстати, как с поддержкой нескольких активитей/окон приложения? Редкий проект ограничивается одним окном…
                                            +1
                                            Вот да, DSL для гуя это мечта, а описывать это кодом это мягко говоря странно.
                                              0
                                              Это отлично. Работают все рефакторинги и автокомплиты, отличная гибкость, можно использовать те же способы декомпозиции что и в коде обычном. Сколько боли в той же нативной разработке под андроид из за использования xml? Жуть.
                                                0
                                                Это называется костыль — вместо того чтобы допилить инструментарий для поддержки читамоего и удобного DSL, делать всё кодом и считать что это хорошо.
                                                  0
                                                  А зачем? Если описание UI во flatter, jetpack compose, swift ui, все равно выглядит достаточно декларативно, но дает при желании гибкость которая многим DSL и не снилась.
                                                    –1
                                                    Если выглядит, то да, но в этом и суть проблемы.
                                                      –1

                                                      KV Language поддерживает код Python и гибок настолько, насколько гибок мягкий пластилин. В коде должна быть только логика и ничего более!

                                                0
                                                Просветите пожалуйста, меня несмышленного, смогу ли при помощи Kivy написать приложение на ТСД, которое будет получать доступ к встроенному 2D-сканеру, терминала?
                                                  0

                                                  Kivy == Python. Почти все библиотеки, которые поддерживаются Python смогут работать и на мобильном устройстве. На десктопе — все!

                                                  0

                                                  Как раз kivy изучаю в данный момент, только ради приложения для себя. И тут как раз эта статья, спасибо вам.

                                                    0
                                                    Все работает спасибо за инструкцию!!!
                                                      0

                                                      Установите Android Debug Bridge. Подключите устройство к компьютеру USB кабелем. Откройте терминал и введите команду:


                                                      adb logcat | grep python

                                                      Запустите приложение на устройстве и смотрите ошибку в терминале.

                                                        0
                                                        Да я забыл kivymd подключить просто)
                                                        А за способ отладки спасибо пригодится!!!
                                                      0
                                                      До недавнего времени сборка apk отлично собирались, но появилась ошибка File "/usr/local/lib/python3.6/dist-packages/buildozer-0.40.dev0-py3.6.egg/buildozer/targets/android.py", line 816, in get_dist_dir
                                                      matching_dirs = glob.glob(join(self._build_dir, 'dist', '{}*'.format(dist_name)))
                                                      AttributeError: 'function' object has no attribute 'glob'
                                                        0

                                                        По этой инструкции все делали?

                                                          0

                                                          Да, только не на виртуалке, а на настоящей убунте. Причем буквально два дня назад apk собиралась без проблем. Видимо это связано с апдейтом python-for-android.

                                                            0

                                                            Сейчас попробую на своей машине...

                                                              +2

                                                              image


                                                              Все прекрасно собирается в виртуальной машине!

                                                                0
                                                                Ошибка была у меня в коде: откатился до предыдущей версии и всё заработало. Хотя если честно не понятно почему buidozer валился
                                                                  0

                                                                  Да, код с ошибкой не будет построен.

                                                                    0
                                                                    Хотя на десктопе всё запускалось без ошибок. Или же buidozer анализирует все файлы в проекте на наличие ошибок?
                                                                  0
                                                                  Выполнял по инструкции. Не могу разобраться в чём дело…
                                                                  image
                                                                    0

                                                                    Изображения вашего нет...

                                                                      0

                                                                      Полностью лог дайте.

                                                              0
                                                              Попробовал прям с примером из статьи, apk собралось. Запускаю на двух смартфонах, появляется Loading… и вылетает. Вечером попробую с Android Debug Bridge разобраться, может станет понятно в чём дело.
                                                                0
                                                                Тоже самое, вылетает
                                                                  0

                                                                  Лог покажите. Что можно ответить на "Тоже самое, вылетает"?

                                                                0
                                                                Символично, буквально недавно узнал про kivy и сделал неделю назад простенькую игрушку для андроида на нем)
                                                                play.google.com/store/apps/details?id=org.corgi.corgi
                                                                Приятный инструмент, здорово, что продолжаете над ним работу.
                                                                  0
                                                                  100MB это не многовато для простенькой игрушки..? Или там видео файлы.
                                                                    0
                                                                    Переборщил, знаю. Я не знаю, как сжимать изображения, там все элементы отрисованы для 4к разрешения и в png формате, поэтому получилось, что каждая кнопка весит по 1 Мб. Я пробовал преобразовать их в gif формат, они сразу становились раза в три легче, но и сильно теряли в качестве и получались мыльными\
                                                                      0
                                                                      ОК, просто думал может это киви так добавил.
                                                                        0
                                                                          0
                                                                          Спасибо)
                                                                          Качество все же падает от этого сжатия тоже, но на экране телефона этого почти не заметно. Обновил версию, теперь весит 35 Мб)
                                                                            0

                                                                            Пожалуйста!

                                                                    0

                                                                    А как дела обстоят с KivyMD Studio?
                                                                    Вроде выглядит приятно, очень хочется пощупать

                                                                      0

                                                                      Этот проект развивается очень медленно, так как я работаю над ним в одиночку. А поскольку это не основной мой проект, плюс основная работа занимает львиную часть времени, то о каких-либо сроках говорить бессмысленно.

                                                                      +2
                                                                      Я извиняюсь. А «loading» на несколько секунд для «Hello world» необходимая стадия? Или это особенность работы именно вашего фреймворка?
                                                                        –5

                                                                        Во-первых, это не МОЙ фреймворк! Во-вторых, это не натив, глазки открой!

                                                                          +4
                                                                          Хамить совершенно не обязательно. Вроде бы на серьезном ресурсе, а не на базаре.
                                                                            –3

                                                                            Так не надо тут троллинг разводить, если не на базаре.

                                                                              +2
                                                                              Я опечален тем фактом, что мой вопрос был воспринят как троллинг. Никакого негатива я в него не вкладывал и интересовался совершенно искренне т.к. просто не имел ранее дела с подобными фреймворками.
                                                                            0
                                                                            > Во-вторых, это не натив

                                                                            на этом, сравнение с флаттером можно и заканчивать
                                                                              0

                                                                              А Flutter, типа натив?! Вам бы пойти почитать, как работает движок Flutter, а не позорится глупыми фразами!

                                                                                0
                                                                                Смотря что считать нативом. Релизная версия компилится в нативный бинарь для целевой архитектуры. А вот дебаг версию которая запускается в dartVM действительно нативом не назовешь.
                                                                                  0

                                                                                  Серьезно? В нативный? И все элементы UI нативные?

                                                                                    0
                                                                                    С т.з. бинарного кода — да. С т.з. «нативной» разработки — нет. Но наверно для вас не будет сюрпризом, но в той же нативной разработке под андроид часто используется AppCompat/androidx в которых по сути содержатся аналогичные нативным вьюшки но переписанные для единообразной работы на разных версиях. Т.е. по сути «нативные» разработчики очень часто используют «ненативные» вью которых нет в SDK. Это даже если про кастомные вью забыть и библиотечные которых нет опять же в SDK, например как в бибилиотеке material.
                                                                                      –1

                                                                                      С этой точки зрения — пакет с приложением Kivy также компилируется в натив, поскольку все сторонние библиотеки собираются под архитектуру мобильных процессоров. Поэтому очень раздражают люди, которые не понимают сути вопроса, но пишут, что Flutter — это натив.

                                                                                        +3
                                                                                        Флаттер это таки натив. Возможно даже больший натив чем та же java под андроид (до появления ART). Потому что исполняется бинарь нативный для процессора и нет никакого интерпретируемого кода на скриптовом языке. Или хотите сказать что у вас python aot компилируется в нативный бинарь для ARM?
                                                                                          –1
                                                                                          Возможно даже больший натив чем та же java под андроид

                                                                                          На этой ноте диалог с вами окончен!

                                                                                            0

                                                                                            Очень конструктивно

                                                                                            +1
                                                                                            Flutter компилируется в нативный код устройства, но ни в каком виде не использует нативные виджеты, т.к. для реализации GUI используется движок Skia, производящий отрисовку графики на холсте. Но я полностью согласен, что ассемблерный код, генерируемый компилятором C++, является нативным для устройства, в отличие от виртуальной машины Java.
                                                                                              0
                                                                                              Ну, в последних версиях таки в общем dex тоже art компилируется aot в нативный для процессора бинарь с учетом профилей исполнения (пусть для первых запусков и может использоваться интерпретация байткода с jit). Это раньше оно далвиком исполнялось и только jit был. Так что оговорка что под андроид нативные приложения не совсем нативные — оно в целом только про старые версии))
                                                                                              0
                                                                                              Еще хотел бы добавить, что фреймворк Kivy работает по тому же принципу, что и Flutter: исходный код транслируется в C++ (благодаря тому, что Kivy написан на CPython и C/C++) для упрощения распространения, а рендеринг GUI приложения происходит при помощи OpenGL.
                                                                                                +1
                                                                                                Странно, если оно таки компилится в нативный бинарь — чего оно загружается долго?
                                                                                                  0

                                                                                                  Исходный код Kivy приложений не транслируется в С++. При компиляции пакета вся ресурсоемкая часть движка Kivy компилируется в натив, так как фреймворк Kivy написан на Cython, а уже все это обернуто в вызовы и API из чистого Python.

                                                                                                    0
                                                                                                    Извиняюсь, был не прав. Действительно, в нативный код компилируются не все модули готового приложения.
                                                                                +2
                                                                                Здравствуйте. Я вот залил один из ваших примеров на телефон. Чёт работает как-то не плавно. И такое ощущение, что притормаживает. Это так всегда?
                                                                                  –3

                                                                                  Аналогично и в примерах на Flutter! Некоторые — просто жуткий стоп кран! В следующей статье я покажу, какой Flutter тормоз. А вам могу ответить только одно — примеры в студию!

                                                                                    +3

                                                                                    Это очень сильное заявление. Какой именно пример на флаттере и на каком телефоне у Вас тормозит?


                                                                                    И да, жду с нетерпением заявленную статью.

                                                                                      0
                                                                                      Может вы его в debug mode запускали? Это две большие разницы
                                                                                        0

                                                                                        Я его из Маркета качал!

                                                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                                  Самое читаемое