Я нашел способ сделать Flutter более приятным… с помощью F#

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

Предыстория

Я использую Flutter с момента первого публичного альфа-тестирования в 2017 году и до сих пор использую его. С тех пор я стараюсь любить его, но безрезультатно. Не может даже понравиться. Я создал приложения, которые есть в магазинах и отлично работают; У меня есть собственный проект SaaS, созданный с его помощью, и я до сих пор боюсь каждый раз, когда мне нужно добавить новую функцию или исправить какую-то ошибку. Почему? Дарт (в основном)… Я ненавижу этот проклятый язык. Единственное, что мне в нем нравится, это async/await и функции верхнего уровня. Вот и все.

Около года назад я начал играть с F#, и он мне сразу понравился. Его функциональное программирование, лаконичный синтаксис, вывод типов, вообще типы… какая радость. Вы можете написать строго типизированный код, который выглядит как динамическая типизация.

Я все больше и больше люблю динамическую типизацию, но это тема для другого дня (подсказка: TDD). Я ухватился за первую возможность использовать его в своем проекте на бэкенде — тогда я открыл для себя Fable. Fable — это компилятор F# -> JavaScript, но какое отношение он имеет к Flutter и Dart? Что ж… в версии 4.0 Fable добавляет поддержку F# -> Dart!

Мы можем написать код F#, скомпилировать его в Dart и использовать как обычный код Dart. На момент написания этой статьи Fable поддерживает не все, но почти готово. Посмотреть его прогресс можно по этой ссылке.

Почему

Прежде чем мы перейдем к тому, как, я расскажу о том, почему. Зачем использовать сторонний инструмент, чтобы делать то, что вы уже можете сделать из коробки и без потенциальных рисков?

Я не люблю Dart, поэтому для меня это не проблема, но я приведу вам несколько примеров.

Примеры

Независимо от того, что вы пытаетесь и что вы делаете, не существует простого или элегантного способа написания классов данных в Dart. Почему? Потому что вам нужно определить свои собственные реализации equals и hashCode. Dart сравнивает по ссылке, а не по значению.

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

Не выглядит слишком плохо? Неправильный.

Вы видите part 'user.freezed.dart';, @freezed, with _$User, UserAuthenticated, UserAnonymous? Но подождите, есть еще. Вам нужно добавить зависимость, написать приведенный выше код, запустить генерацию кода из терминала, и не говоря уже о том, что он создаст для вас отдельную папку. Ага. Поскольку код слишком длинный, вы можете проверить его самостоятельно по этой основной ссылке.

Если вам нужна сериализация/десериализация JSON, вы получите еще один сгенерированный файл… ах, это просто беспорядок.

Излишне говорить, что это некрасиво.

Но нам не нужна библиотека для такого простого класса.

Это в основном то же самое.

Примечание. Заморозка действительно улучшает опыт разработчиков при создании и использовании классов данных, объединений, копирования и т. д.

Вам необходимо обновлять методы == и hashCode каждый раз, когда вы добавляете поле/свойство. Мы можем сделать лучше. В F# это выглядит следующим образом:

Вот и все? Да это оно. Когда вы запускаете это через Fable, вы получаете следующее:

Примерно то же самое, что мы писали ранее. Вы получаете переопределения == и hashCode. Это все, что вам нужно в Dart, но с гораздо более приятным синтаксисом F#. Самое приятное то, что вам никогда не придется смотреть на этот код, только на красивый код F#. Я покажу вам, как сгенерировать Dart из F# за секунду.

Бизнес-логика и обработка состояний

BLOC — очень популярная библиотека для управления состоянием, и я использую ее в своем проекте. Чтобы использовать BLOC в F#, мне нужно было бы создать некоторые привязки и т. д., но я этого не сделал, потому что еще не знаю, как это сделать, поэтому я написал почти то же самое, не используя библиотеки. Обычный старый Редукс. BLOC — это Redux, то есть Elm Architecture, а F# имеет гораздо лучшую и более чистую реализацию.

Это информативная статья. Я буду упрощать вещи для удобочитаемости, чтобы передать свои идеи, не нуждаясь в технических знаниях. Я подробно расскажу и покажу вам, как написать приложение Flutter исключительно на F# в моей следующей статье.

А пока давайте рассмотрим еще один пример и увидим разницу.

Красивый код, правда? Нулевой жирный код, ничего лишнего, и он отлично читается. Теперь, когда вы запускаете это через Fable, вы получаете следующий код. Пожалуйста, нажмите на эту ссылку, чтобы увидеть суть.

Да, я знаю, что это сгенерированный код, так что он уродливее, чем если бы я/вы написали его, но, как показано выше, это не так уж и отличается. В основном названия разные. Представьте себе это с заморозкой — если бы каждый класс/тип был в отдельном файле, у вас было бы восемь дополнительных файлов, содержащих сгенерированный код. Имейте в виду, что этот код не включает StateStore/Cubit/Bloc, который занимает около 10 строк в F# и больше в Dart. Внедрения зависимостей нет, но об этом я расскажу в следующей статье.

Как

Может быть, я убедил вас, может быть, вам просто любопытно, а может быть, вам уже нравится F# и вы хотите использовать его для мобильной разработки. Я не знаю, но давайте посмотрим, как скомпилировать F# в Dart и запустить наше приложение Flutter.

  1. У вас есть существующий проект Flutter.
  2. Вы хотите построить совершенно новый проект.

Это одинаково для обоих подходов. Вам нужно следующее:

  1. IDE для Flutter -> Android Studio или VS Code
  2. IDE для F# -> Intellij Rider или Visual Studio
  3. Установлен .NET

Откройте IDE F# и создайте новый проект F# внутри вашего проекта Flutter. Назовем его fsharp.

Создайте каталог .config внутри проекта, а внутри него создайте папку с именем dotnet-tools.json, содержащую следующее:

{
  "version": 1,
  "isRoot": true,
  "tools": {
    "fable": {
      "version": "4.0.0-theta-018",
      "commands": [
        "fable"
      ]
    },
  }
}

Вы можете использовать NuGet для добавления пакетов F#.

Откройте терминал и выполните две команды одну за другой:

  1. dotnet tool restore
  2. dotnet fable watch YOUR_F#_PROJECT_DIR_NAME -o lib --lang dart

Во второй команде lib — это место назначения скомпилированного кода, то есть ваш каталог lib Flutter.

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

Ладно, на этом эта часть заканчивается. Буду рад узнать, что вы думаете об этом подходе. Мне интересно, что думают любители Flutter, и, конечно, если вы разработчик F#, выглядит ли это привлекательнее, чем Fabulous или Xamarin?