Пролог

Кто-нибудь пробовал использовать Dart / Flutter как на клиенте на нескольких платформах, так и в качестве сервера? Кто-то, конечно. пробовал, хотя далеко не каждый за этим приходил к Flutter. Я на своём pet-проекте провёл такой эксперимент, и хотел бы поделиться результатами и выводами.

Так уж вышло, что тема кроссплатформы преследует меня довольно давно:

  • Ещё в институте занимался разработкой десктопных приложений на Qt4. В дальнейшем пробовал возможности Qt на мобильных устройствах на примере фреймворка Felgo.

  • Сам разработал и вывел в продакшн несколько информационных мобильных приложений на DrupalGap и Ionic.

  • В рамках одного из стартапов моей компании реализовано простенькое мобильное приложение на Vue-фреймворке Quazar.

  • Для одного из клиентских проектов делали Desktop-версию React-приложения, заворачивая его в Electron. А позднее и адаптировали под мобилки (вернее, электронные доски в школах, но там ведь Android).

Поэтому, прочитав многочисленные материалы о возможностях Flutter работать на всех платформах, я уже не мог оставаться равнодушным, хотелось лично познакомиться с технологией, хотя бы поверхностно, и понять, насколько и правда короссплатформенна её кроссплатформа?

В этой статье не будет столь любимых сообществом обсуждений, как же на самом деле надо хранить состояние, какова идеальная архитектура приложения и т.п. Более того, я бы сказал, что многие архитектурные и инфраструктурные решения, описанные здесь, крайне не рекомендуются к применению. Однако, они демонстрируют возможности Dart и Flutter работать в различных условиях, и именно в этом я вижу их ценность. 

Наш подопытный

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

К счастью, у меня под рукой была «реальная» задача из жизни: «оцифровать» настольную игру, суть которой – совместное составление единой истории. Чем-то напоминает Dungeons & Dragons, но все-таки существенно от него отличается: есть три колоды карт (место, персонаж и событие). Перед стартом игры выбирается тема повествования, а дальше игроки по очереди тянут карту из любой колоды и продолжают начатую гейм-мастером историю, подстраиваясь под рандом, который выпал им в карточке. Собственного персонажа никто не имеет, можно в принципе вытворять что угодно с любым аспектом вселенной – лишь бы это соответствовало вытянутой карте. Степень серьёзности повествования определяется игроками «на берегу», тут можно как упороться, так и попробовать серьёзно что-то моделировать.

Как собраться оффлайн и тянуть карты из колоды, когда всех рассадили на карантин в пандемию? Мы пробовали показывать карты в камеру Zoom, но по причине очень разного качества связи у игроков это была плохая идея. Кроме того, даже в оффлайне бывали случаи, что народ собрался, и мы бы даже сыграли, но обладатель колоды карт не пришел, или карты с собой не взял. Обидно, однако!

Часть 1. Backend. Telegram-бот

Написать чат-бота – наверное, самое простое на сегодняшний день решение, если нужно сетевое взаимодействие между игроками, а игра из себя представляет что-то вроде текстового квеста. Бэкенд и база данных для такой задачи практически не нужны. Впрочем, я пытался сделать по-разному… не вдаваясь в подробности моих исканий в процессе разработки, дам обратную связь по технологиям и библиотекам. С которыми мне довелось при этом работать:

  • TeleDart — библиотека для создания бота для Телеграм. Впечатления положительные, работает как в режиме long-polling, так и веб-хуком. Дорабатывается и обновляется автором, но не могу сказать, что прям «динамически развивается». Впрочем, оно и к лучшему, будет стабильнее код, лишь бы автор не забросил.

  • Dartis — клиент Redis для Dart. Отлично работает, но вот как раз тот случай, когда либа была написана однажды, и далее заброшена автором. Уже сейчас в ней нет поддержки null-safety, что исключает беспроблемное использование в своих проектах, а в перспективе оно будет всё менее и менее совместимо с актуальной версией Dart. Да и не все возможности Redis поддерживаются. Альтернативные либы есть, но они ещё в более плачевном и заброшенном состоянии, чем эта. Хотя в целом впечатления от использования приятные, в последствии решил всё-таки его выпилить и хранить всё, что нужно, в переменных внутри приложения, хотя это и не очень круто, особенно на случай креша.

  • Mysql1 - ещё одна моя попытка найти постоянное хранилище данных, более-менее традиционное для серверных приложений. Увы, и тут беда: либа работает, но функционал также в полудописанном состоянии. Тем, кто привык к мощным ORM, будет больно и грустно. Впрочем, у меня возникли проблемы даже с обработкой собственноручно написанных SQL запросов: в библиотеке не реализована обработка некоторых типов колонок (в моём случае был blob), а такие колонки просто игнорируются в выборке. Печально, но, боюсь, это направление тоже ещё очень долго будет не юзабельным, если вообще не отомрёт.

  • Firebase. Это решение я не использовал, хотя, похоже, тут как раз всё хорошо. Но меня не устроил вариант коммититься на хранилище данных, которое я не смогу, в случае необходимости, перенести на свой хостинг. Кроме того, оттолкнула очень долгая схема авторизации, в то время как у меня простое серверное приложение, и достаточно было бы авторизации по секретному токену. Простите мне мою лень.

  • Parse server SDK. Вот этот вариант показался мне гораздо более дружелюбным, в итоге и остановился на нем. Есть куча облачных продуктов, к которым можно подключиться на бесплатном тарифном плане, если не хочется поднимать у себя. Но и у себя можно поднять opensource-сервер, который будет преспокойно работать по единому стандарту API. Библиотека поддерживает (де)сериализацию объектов, есть гибкое API для поисковых запросов, а в конечном итоге это всё может сохраняться как раз в SQL базу данных. В общем, похоже, я нашел, что искал, пусть и через «сервис-посредник», но по крайней мере опенсорсный.

  • Пакеты File и Archive - пользоваться просто и удобно, мне нужно было всего лишь распаковать набор карточек из zip-архива, прочитать JSON и отправить это на сервер, превратив в сущности Parse.

В конечном итоге мой телеграмм-бот использовал пакет на основе TeleDart - teledart_app — который я сам же и написал. Parse server SDK для общения с бэкендом, API для работы с файлами и архивами.

Впечатления от бэкенд-разработки бота:

  1. Благодаря системе типов крайне приятно, что код сразу работает ровно так, как задумано… если, конечно, не делать все переменные dynamic – тогда Dart превратится в PHP с его худшей стороны.

  2. Нужно ловить исключения, причём отдельно в синхронном, а отдельно в асинхронном коде. И с асинхронным кодом сложнее, т.к. там нельзя просто обернуть всё содержимое main в try – catch и закрыть тем самым все проблемы. Приходится каждый асинхронный вызов «лично» оборачивать, это и утомительно и загромождает код.

  3. Привычных на бэкенде хранилищ для данных нету, для продакшн-проекта это может стать решающим аргументом «против», т.к. невозможно легко и быстро подвязаться к имеющейся инфраструктуре.

Сам бот можно пощупать тут, написав ему в личке /startgame, либо собрать в телеграмме группу, добавить туда бота и запустив игру на несколько игроков.

Часть 2. Backend. REST API

С точки зрения бэкендера могу сказать, что возможность создавать API, хотя бы простейший REST – это куда важнее всяких там ботов. Поэтому следующим шагом я вынес из телеграмм-бота основную логику игры в REST-сервер. К тому же хотелось иметь «слой бизнес-логики», который не будет ничего знать о том, в каком конкретно приложении его используют.

В поисках библиотеки для API-сервера гугл, видимо по старой памяти, навёл меня на Aqueduct. Впоследствии выяснилось, что его поддержка и развитие давно прекратили, библиотека, можно сказать, мертва. Ещё один неприятный пример, как у «всеплатформенного» языка отмирают «непрофильные» библиотеки.

В итоге решил остановиться на Shelf – по крайней мере есть впечатление, что это решение будет жить долго и счастливо и не закроется завтра.

По функционалу rest-сервера возможности минимальны. Если сравнивать с возможностями даже самого простого php-фреймворка, то в Dart всё выглядит бледно и печально. Все-таки мне показалось, он максимум годится для создания крохотных сервисов, решающих одну-несколько мелких задач, без обширного API и «сложносочинённых» запросов. В частности, мне не понравилось негибкость в настройке роутов, без дополнительных пакетов тут не разрулиться, а кто знает, не постигнет ли эти пакеты завтра судьба того же Aqueduct? Подход к унифицированной валидации данных запроса тоже пришлось выдумывать и реализовывать самому.

Зато для Shelf было приятно писать тесты. Прежде всего потому, что не нужно было поднимать сервер отдельно и слать к нему настоящие запросы. Shelf может быть запущен как просто класс, не слушающий никакой адрес и порт, а принимающий данные прямо из кода. Этим я и воспользовался здесь. Хотя, если очень хочется, то можно и поднять сервер в изоляте перед запуском тестов...

Часть 3. Backend. Стабильность и потребление памяти

То, что телеграмм-бот у меня работает по схеме long-polling, уже само по себе исключает тестирование производительности, т.к. сколько бы запросов в очередь ни поставили, бот всё равно будет обрабатывать их «в своём темпе», соответственно не сильно и нагружая rest-сервер.

Кроме того, для правильного теста на производительность хорошо бы с чем-то сравнивать, а я не готов переписывать проект повторно на том же PHP или node.js, чтобы померяться с ними скоростью.

Поэтому посмотрим только память. Прежде всего, взглянем на данные мониторинга на production-площадке:

Данные за два месяца
Данные за два месяца

Конечно, у меня там не сотни пользователей, но видно, что за два месяца работы оба бэкенд-сервиса не сильно выходили за свои рамки по памяти. Просадка на верхнем графике скорее всего связана с пересборкой и перезапуском docker-контейнера, в остальном телеграмм-бот занимает 60-70 мегабайт и большего не просит.

«Лесенка» на нижнем графике – это rest-сервер. Он существенно легче и занимает гораздо меньше памяти, и тут явно прослеживается особенность работы GC в Dart: он не станет лишний раз тратить ресурсы на освобождение памяти, если потребление памяти значительно не выросло.

Чтобы посмотреть на работу GC «в лабораторных условиях», я написал фейковый телеграмм-сервер, который запускал одновременно 100 игр по 5 игроков, и они там немного играли, после чего все игры завершались – должна была происходить очистка ресурсов. Не такая уж большая нагрузка, но на бОльших значениях приходилось слишком долго ждать (long polling же, писал выше), и «DevTools Memory page» совсем зависала задолго до окончания процесса.

Вот что я увидел на графике телеграм-бота:

График расхода памяти при двух циклах запуска фэйковых игр.
График расхода памяти при двух циклах запуска фэйковых игр.

Обратите внимание на выделенную позицию на таймлайне. В первой версии теста именно здесь у меня завершались все начатые игры, но очистки памяти при этом не происходило. Dart продолжал занимать ненужные ресурсы, не вызывая GC, т.к. не происходило существенного роста потребления этих самых ресурсов. Чтобы заставить Dart очистить занятую память, пришлось запустить второй проход «тестовых игр». И только когда пошли новые запросы и потребовалось ещё больше памяти, пришел GC и очистил занятое предыдущими 100 играми место.

Хорошо это или плохо – ну, для серверного приложения, особенно если там запущен ряд конкурирующих за ресурсы процессов, явно не очень. В целом, можно сделать вывод, что хоть утечек памяти и нет, но толком контролировать её расход вы тоже не сможете. Вручную запустить GC в продакшн-сборке способа тоже нет, так что остаётся полагаться только на то, что у вашего железа достаточно памяти и на неё нет других претендентов.

От запуска к запуску поведение GC может меняться – видимо, зависит от других процессов, запущенных на машине. Например, сразу же запустив тест повторно, для телеграм-бота я получил уже немного другой график с «артефактом»:

Другая динамика расхода памяти при тех же условиях тестирования
Другая динамика расхода памяти при тех же условиях тестирования

С REST-сервером картина сходная, но из-за относительной «разгруженности» сервера GC туда приходит крайне редко. Так что, после того как приложение «набрало килограммы», активно выполняя запросы, оно так и остаётся висеть со всем лишним хламом в памяти. Я так и не дождался автоматической очистки. Это всё так и будет болтаться балластом в памяти до ближайшего возрастания нагрузки... На скриншоте как раз показано место, где я не вытерпел и вручную вызвал GC:

В конце графика видно, что GC мог бы освободить приличный кусок памяти, если бы "захотел"
В конце графика видно, что GC мог бы освободить приличный кусок памяти, если бы "захотел"

Вот снимок памяти до ручного вызова GC:

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

Выделенные сущности уже должны быть удалены, но Dart всё ещё держит это в памяти. И только после ручного GC это всё исчезает, остаётся только два вида сущностей, которые специально закешированы:

После очистки осталось только 12 экземпляров классов.
После очистки осталось только 12 экземпляров классов.

Часть 4. Frontend. Мобильное и прочие приложения

Фронтенду и так посвящено достаточно внимания, так что обозначу самое основное:

  1. В разработке я решил не использовать ничего, кроме setState. Такое бешеное обилие стейт-менеджеров, каждый ориентирован на какие-то привычки фронтендеров с неведомых мне фреймворков… Меня же, как ex-бэкендера, setState вполне устраивает, всё прозрачно и понятно.

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

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

Первый же эксперимент, который был проведён – это вживление REST-сервера внутрь приложения. Я выше писал, что shelf может и не принимать данные по сети, а получать напрямую JSON на обработку. Вот это и позволило просто взять готовый слой «бизнес-логики» и воткнуть его в качестве зависимости без каких-либо правок под платформу. Это не самое хорошее решение для production, но именно как иллюстрация универсальности кода на Dart – отлично подойдёт.

Что меня удивило в процессе разработки, так это то, что такие естественные вещи для мобилок, как управление вибрацией и звуком – это не часть Flutter, а библиотеки, написанные сообществом. Но в целом всё работает.

Так же у приложения есть блок загрузки карт для оффлайн использования… здесь удалось поработать с изолятами (чтобы не использовать платформо-зависимые плагины для мобилок).

Кроме вибрации, звука, фоновой загрузки в изоляте и встроенного rest-сервера, ничего технически примечательного в приложении нет – обычные экраны с material-виджетами и анимациями. Получившуюся программу удалось собрать:

  • Под Android

  • Под Windows. Здесь пришлось сделать одну платформо-ориентированную вставку, чтобы по кнопке «Выход» вызывался «exit()», а не SystemNavigator.pop(). И добавить перехват исключения на вызов функций модуля Vibration.

  • Под Web. Здесь пришлось в принципе спрятать кнопку выхода, а также убрать из интерфейса кнопку для загрузки карт в оффлайн, потому что изоляты в вебе не работают. И немного пошаманить с HTML, чтобы было подобие Splash Screen. Кроме того, относительно новый модуль для вибрации пришлось заменить на относительно старый, но с поддержкой веба. Плюс были проблемы с .env файлом – до меня долго не доходило, что не открывается он по сети именно из-за точки, и нужно просто переименовать.

Под Linux не делал сборку, но и не вижу смысла, потому что серверная часть уже работает на Linux, и там всё в порядке – тест на кроссплатформенность считаю пройдённым. Ну и сборку под Mac / iOS не сделал и не смогу сделать, потому что у меня неть.

В заключение добавлю, что для любой платформы собранное приложение обладает весьма скромным весом – от 10 до 34 мегабайт в зависимости от платформы. Сравнивая с решениями на Cordova, особенно если выкачать Crosswalk – это 10 и более мегабайт просто на HelloWorld, сравнивать с решениями на Qt так вообще страшно – там за сотню перевалит.

Часть 5. Релизы

В целом ничего особенного. Для серверных приложений без проблем можно собрать всё в Docker, как и было сделано для REST-сервера и Telegram-бота. Для андроида всё необходимое описано в официальном мануале.

Повозиться пришлось разве что только с Windows. Прежде всего, встал вопрос инсталлятора, и тут выяснилось, что их целый зоопарк, а прорваться в маркетплейс в качестве разработчика не так-то просто, как в Google Play. Моя заявка до сих пор «на рассмотрении», уже месяца три или четыре …

Второй сюрприз был связан с внешними плагинами. Для проигрывания звуков у меня используется kplayer, набор биндингов для VLC. И с момента добавления этой зависимости в релизной сборке у меня теперь появляется каталог на 335 мегабайт со всеми возможными кодеками для VLC!! Дело не только в размере, а ещё и в где-то 5-секундном фризе перед первым проигрыванием файла, потому что все эти «300 спартанцев» разом начали ломиться в Фермопильский проход загружаться в память… К сожалению, более быстрого/адекватного способа, чем «удалять лишние файлы, пока звук не перестанет работать» я не нашел… в итоге осталось 4 dll суммарно на 15 мегабайт. В принципе, я-то не умер, просто несколько демотивирует тот факт, что 15 лет назад приходилось заниматься тем же самым, и с тех пор прогресс не шагнул вперёд…

Часть 6. Выводы

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

Простите, я не художник
Простите, я не художник

Плюс бизнес-логика используется для всех одна, из единой кодовой базы.

Моё личное мнение – какими бы болячками ни болели сейчас Dart и Flutter, на данный момент это наиболее удачное кроссплатформенное решение, которое я видел. Для портирования на другие платформы требуется минимальное количество усилий, много кода можно просто таскать копипастом, и это будет работать везде одинаково хорошо. Да, не хватает тонкого контроля, нативным разработчиком, судя по отзывам – за нативным функционалом, меня лично вот ситуация с памятью расстроила.

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

Комментарии (20)


  1. comerc
    11.02.2022 23:16
    -1

    А кто-то меняет фронт с Dart на TypeScript+React... ))

    Flutter - наше всё, конечно. Но просто попробуйте GoLang.


    1. ASGAlex Автор
      11.02.2022 23:19

      У всех задачи разные. На многих вполне серьёзных проектах вообще всё по-старинке, html+php+js, и на то есть свои веские основания.


    1. raitonoberu
      13.02.2022 13:19

      Простите, а как на Go фронт писать?


      1. McCoder
        13.02.2022 17:23

        Сайт написанный на GO https://go-app.dev/

        Исходники https://github.com/maxence-charriere/go-app


  1. sergeymolchanovsky
    11.02.2022 23:19
    +6

    В разработке я решил не использовать ничего, кроме setState. Такое бешеное обилие стейт-менеджеров, каждый ориентирован на какие-то привычки фронтендеров с неведомых мне фреймворков…

    1. Можно помахать ручкой производительности.
      Стейт-менеджеры точечно ребилдят на экране только то, что нужно. setState заребилдит все, до чего дотянется. Если у вас на экране список с 1000 элементами - они все ребилднутся.
      А если, не дай бог, вы setState будете вызывать, например, в onSliderUpdate...

    2. setState хорош ровно до того момента, пока вам не понадобится, находясь на одном экране приложения, поменять что-то на другом экране. Или синхронно отображать одни и те же данные в нескольких местах.

    Тем более странно, что человек, который так тщательно анализирует расход памяти на бэке и вызовы GC, покрывает приложение тестами - и наплевал на производительность на фронте.


    1. ASGAlex Автор
      11.02.2022 23:27

      Согласен, поскольку эти подозрения меня самого никогда не покидали.

      Но когда у тебя такой богатый выбор инструментов, как понять, что А будет лучше Б, С и Д? Про какой ни прочтёшь, так они все хорошие, а значит обязательно где-то притаились грабли :-)

      А раз пока нет четкого понимания, что именно мне требуется, кроме простоты, то считаю лучшим выбором взять и опробовать то, что лежит в основе. По крайней мере пока что привычка "искать недостающие ответы в исходниках ядра" меня ещё не подводила :-)


    1. sarexer
      12.02.2022 02:05
      +1

      А что мешает setStateом обновлять точечно? А для прокидывания изменений по дереву виджетов использовать inhiretedwidget или provider? Проблема setstate только в том что логика от ui не отделена. В производительности ты ничего не проигрываешь


      1. ASGAlex Автор
        12.02.2022 02:19

        Ну setState обновляет весьма массово - всё дерево виджетов, которое под ним...

        С другой стороны, разработчики говорят, что делает он это весьма экономно, всё объекты, которые можно сохранить, система сохраняет, просто цепляет старые инстансы состояния к новым инстансам виджетов, не более.

        Понятно, что стрелять setState как из пулемета - не очень хорошо.

        С другой стороны, вот у меня в приложении виджет с карточкой, он одновременно может проигрывать анимацию переворачивания, перетаскивания, а сверху ещё виджет-прелоадер. И всё это я оптимизировал примерно никак - просто использовал готовые модули, заворачивая один написанный кем-то виджет с анимацией в другой, тоже с анимацией... А каждый кадр анимации - это, на секундочку, setState... Нууу, согласен, в веб-версии оно, пожалуй, уже лагает...

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

        Иначе это будет просто слепое копирование паттерна, "потому что мне так в учебном курсе сказали".


        1. vadlit
          12.02.2022 07:12
          +1

          Под капотом все популярные стейтменеджеры - это тот же InheritedWidget + StreamBuilder. Разница лишь в api и в том, что же хранится в InheritedWidget. Рокетсайнс никакого нет, достаточно разобраться с этими штуками, чтобы легко по своему вкусу уже выбрать между готовыми решениями. Изучать внутреннее устройство каждого решения нет необходимости.


  1. pecheny
    12.02.2022 00:32

    Моё личное мнение – какими бы болячками ни болели сейчас Dart и Flutter, на данный момент это наиболее удачное кроссплатформенное решение, которое я видел.

    Под критерии, которые вы описали, определенно, лучше подходит Haxe. Не смотрели?


    1. ASGAlex Автор
      12.02.2022 00:42

      Честно сказать, впервые от вас вот и услышал.

      Конечно, не дело давать "экспертные заключения", посмотрев сайт проекта с мобильника, но похоже, что это решение больше подходит под шеринг бизнес-логики, а уже слой интерфейса придется пилить каждый раз заново, используя доступный для каждой платформы свой API...


      1. pecheny
        12.02.2022 01:10
        +2

        Это ошибочное впечатление, под Haxe есть полностью переносимые решения с очень разными подходами. Начиная c haxeui и до более «игровых» решений, которые построены поверх графических API, и при транспиляции будут использовать нужный – OpenGl / ES / WebGl / Vulkan (Kha).
        В результате, интерфейсы можно шарить не только под веб-мобайл-десктоп, но еще и под консоли типа Switch и PlayStation.
        Кроме того, можно взять тот же React Native, для которого есть экстерны под Haxe.


        1. ASGAlex Автор
          12.02.2022 01:46

          Спасибо, интересно! И правда, с беглого взгляда наискосок такого не узнаешь.

          А вы на нём, если не секрет, какие вещи создавали?


          1. pecheny
            12.02.2022 02:34
            +2

            Как наемный сотрудник работал над довольно крупными мобильными стратегиями, но там как раз была шаред-бизнес-логика, клиент с интерфесами – на юнити, а сервер на ноде. Там же на Haxe еще тулинг делал: обвязка для CI, веб-смотрелка логов.

            В качестве хобби кучу всего, игровые прототипы, утилиты, в том числе с использованием c/c++ библиотек. Периодически попиливаю свою библиотеку для лэйаута (в том числе, интерфейсов). Кое-что есть на гитхабе, там ник такой же, как и здесь.


  1. vadlit
    12.02.2022 07:05

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

    Не совсем понял какие сложности с авторизацией у Firebase. Там же можно просто скачать JSON с приватным ключом от сервисного гуглового аккаунта и положить его в ресурсы. Для самого взаимодействия с фаером использовать при этом их SDK. Все. Для типовых задач за глаза должно хватить


    1. ogregor
      12.02.2022 10:58

      Я думаю нормальный бекендер, без труда прочитает код dart, а если он знаком с node.js, то ему только стоит привыкнуть, что некоторые функции языка немного по другому называются.


      1. vadlit
        12.02.2022 11:22
        +2

        Дело не в языке. Язык это меньшая проблема для опытного разработчика. Дело в экосистеме. Найти бэкендера на джаву, сишарп или го какой-нибудь будет на порядки проще. Не потому что эти языки лучше, а просто потому что вокруг них очень много всего есть


  1. Kioju
    13.02.2022 18:40
    +1

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

    Пара вопросов:
    1) какая самая неожиданная вещь была для вас во флаттере? что-то вроде "шоооо, зачем именно так?!"
    2) приходилось ли руками подправлять код js после билда веб-версии? (Мне - регулярно приходится, что б работал скролл внутри фрейма)
    3) как вы обходите то, что при импорте html библиотеки не билдится моб версия? (Мой рабочий вариант - комментирование импорта, так как условный импорт не спас совсем)
    4) попробуйте не поднимать каждый раз новый стек роутов, так как по кнопке "назад" я возвращаюсь в пред экран, хотя он по идее уже не должен быть доступен. Или так и задумывалось?
    5) setState и streamBuilder хватает для большинства задач, особенно при нормальной доке, те камрады, что говорят про setState и "перерисовку всего" не учли логику рендера флаттера или злоупотребляют ключами виджетов. Конечно уже на крупных проектах нужно что-то просто заставляющее технически всех писать в схожем стиле.
    6) а где брали дизайн для карточек?
    7) андроид тоже можно билдить в докере


    1. ASGAlex Автор
      13.02.2022 19:38
      +1

      1. Самым неожиданным, как я уже говорил выше, был вау-эффект от того, что всё сразу работает, как задумывалось, ещё при первом запуске. Я бОльшую часть своего кода в жизни написал на PHP, и там вот довольно типичная ситуация, что написал один раз - теперь начинай гонять свой код в различных условиях, потому что где-то обязательно в переменной будет совсем не то, что ты ожидаешь. При этом сам ты можешь писать просто идеальный код, но достаточно одной какой-то или довольно старой библиотеки, или либы, которую автор написал, спустя рукава, несколько неопределённых аннотаций - и даже максимально строго настроенный линтер в IDE уже не спасёт. А у многих он настроен же вообще кое-как, если вообще есть привычка обращать внимание на его предупреждения. В Dart же такие вольности невозможны, как бы ни был убог твой личный редактор кода - проект просто не скомпилится. И это хорошо!
        В остальном я бы не сказал, что язык меня как-то удивил своими синтаксическими возможностями. Даже напротив, после PHP я чувствовал себя "немного связанным", но со временем научился выкручиваться. Больше всего меня удручает система наследования, а именно то, что статические методы и свойства не наследуются. Просто раздражает каждый раз копипастить один и тот же кусок кода, чтобы сделать класс синглтоном. И ещё раздражает необходимость прокидывать каждый именованный параметр в конструктор класса-родителя, когда наследуемся. Сам Flutter вдоль и поперёк завален этими простынями кода, в котором просто параметры прокидываются из одного конструктора в другой. И при этом разработчики не морьщась заявляют, что они борятся с лишним кодом, они за простоту, лаконичность и так далее и так далее... Как так?! Так что дизлайков Dart от меня тоже получил.

      2. Нет, руками ничего не правил, чему, признаться, был приятно удивлён. Я ожидал, что мне придётся искать какой-то способ выкинуть из проекта импорт класса, работающего на изолятах, но даже этого не пришлось делать: проект прекрасно работает, только при рендере зависимого виджета выскакивает эксепшн. Скрыл зависимый виджет - решил проблему.

      3. Не возникало такой ситуации. В целом я очень осторожно подходил к выбору библиотек на проект, если было подозрение, что либа "слишком web-ориентирована" или "слишком мобильно-ориентирована" - я старался найти альтернативы.

      4. Ага, понимаю, о какой проблеме вы говорите :-) Я её видел, но мне показалось несущественным, пожтому просто махнул рукой. Ведь скорее всего все эти экраны с документацией при регулярном использовании просто поотключают да и всё. А уж ребятам, кто раньше игры вёл, такие вещи и вовсе не нужны. Более насущная пробелма вскрылась, когда мы собрались тестировать игру в реальных условиях. Предполагается, что мобильник с картами будет передаваться по кругу между игроками. И несколько раз вознгикла ситуация "Ой, я тут случайно кнопку нажал, разблокируй телефон обратно, плиз". Телефон разблокировали, а андроид тем временем прибил приложение. Не страшно. если играем без "магии" - ну, перемешабтся карты, переживём. Но мы играли с "магией", то есть там игрок мог оставить "бомбу", которая "взорвалась" бы через несколько ходов и принудила бы дроугого игрока менять свою историю в соответствии с заданными условиями. И вот это всё потерялось. Тут -то я и понял, что совсем забыл о сохранении состояния, чего на мобилках забывать непростительно.

      5. Лайк :-)

      6. Да попросил у ребят скинуть мне сканы колод, которые у них уже были в бумаге. Где уж они их брали - не ведаю, наверняка просто надёргали как-то из интернета. Вообще в былые времена движуха была куда активнее, у многих были "кастомные" колоды под себя, даже специально тренировали гейм-мастеров, проводили "экзамены", но потом @may-cat стало скучно всем этим заниматься, и я могу его понять :-) На самом деле я даже не знаю, когда точно всё это начиналось. Ну, лет 10 назад как минимум, потому что тогда мы с Игорем только познакомились. Но, скорее всего, ещё больше.

      7. Не пробовал, но уверен, что да... эхх, вот бы под Mac/iOS можно было бы билдить в докере и тестить на эмуляторе... Ну, для друзей-яблочников, собственно, я и поднял web-версию.


  1. Skandy77
    14.02.2022 20:00

    Очень даже работает похожий подход, например мы разрабатываем огромную сложную платформу для анализа данных, научных вычислений и интерактивной визуализации на Дарте: https://datagrok.ai

    Если кому-то интересны детали, вот здесь наше выступление на Дартапе как раз на эту тему: https://www.youtube.com/watch?v=lDcTVlfJoFg&ab_channel=WrikeTechClub