Как стать автором
Обновить

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

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

Видимо, я еще более больной на голову человек, потому что считаю, что система построения UI во флаттере – это едва ли не лучшее решение на рынке: статическая типизация, автодополнения от IDE, все фишки языка + не надо отдельно изучать язык разметки, 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) => 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),
              ),
            ),
          ),
        ),
      );
}

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

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

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

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


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()
pos: self.center[0] — dp(25), self.center[1] — dp(25)

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


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

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

Если от скобочек начинает рябить в глазах, то это хороший показатель, что надо выносить код в отдельные виджеты/функции. Этим и хороши разметка в коде и общий принцип 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.

тот же React и JSX

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

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

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

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

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


image

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

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

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

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

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

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

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

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

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

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

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

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

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


image


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

image

Если брать 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
          ]
        ),
      ]
    ),
  ]
)

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

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

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

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


image

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

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()
Спасибо.
Вот посмотрел я на ваш код, скажите пожалуйста, как обстоят дела с автокомплитом и поддержкой IDE? По сути это все один большой комментарий для меня на данный момент, было бы логичней делать что-то вроде шаблонов в Django/Jinja2 где файл шаблона лежит отдельно и наполняется контекстом. Сильно не пинайте, если говорю глупости. Просто как это рефакторить в случае чего, я не представляю.

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

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

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

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

Как вариант:
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%, внутри два блока прижатые к краям, левый из двух '
      'горизонтальных элементов, правый из двух вертикальных. '
      'Без стилей, просто разметка.';
}

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

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

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

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

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

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

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

С вашей точки зрения существует только два мнения — ваше и не правильное?
НЛО прилетело и опубликовало эту надпись здесь

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

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

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

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

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

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

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

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


adb logcat | grep python

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

Да я забыл kivymd подключить просто)
А за способ отладки спасибо пригодится!!!
До недавнего времени сборка 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'

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

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

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

image


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

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

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

Хотя на десктопе всё запускалось без ошибок. Или же buidozer анализирует все файлы в проекте на наличие ошибок?

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

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

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

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

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

Пожалуйста!

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

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

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

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

Я опечален тем фактом, что мой вопрос был воспринят как троллинг. Никакого негатива я в него не вкладывал и интересовался совершенно искренне т.к. просто не имел ранее дела с подобными фреймворками.

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации