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

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

В Android и iOS мы используем каналы платформы для отправки и получения сообщений с собственной платформы, и мы можем использовать PlatformViews для отображения собственных представлений пользовательского интерфейса на Flutter. Но как это сделать во Flutter Web?

JavaScript и Дарт

До того, как Flutter был представлен публике, Dart использовался для создания веб-приложений (как вы можете видеть в этой статье 2013 года - Разработка современных веб-приложений с помощью Dart от Seth Ladd), что было возможно благодаря двум его особенностям. :

  • Возможность компилировать код Dart в JavaScript;
  • совместимость с JavaScript-Dart через пакет js, который позволяет нам вызывать код Dart в JavaScript.

Это означает, что у нас есть прямой способ связи с JavaScript без необходимости PlatformChannels

Чтобы использовать пакет js, мы должны объявить в отдельном файле функции в JavaScript, которые нам нужно вызывать с помощью аннотации @JS.

В качестве примера давайте посмотрим на документацию и посмотрим, как можно использовать JSON.stringify в Dart:

  • Оператор library имеет аннотацию @JS(), за которой следует оператор импорта js.dart.
  • @JS используется, чтобы указать, какую функцию мы вызываем, в данном случае JSON.stringify. Обратите внимание, что объявленная функция не имеет тела и имеет вид external, о чем вы можете узнать больше в этом ответе StackOverflow.

Отображение окна предупреждения о флаттере

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

Чтобы использовать его, нам необходимо использовать следующий код в JavaScript:

В нашем приложении Flutter мы можем захотеть дать ему другое имя, например showConfirm, и для этого мы можем использовать аннотацию @JS, как в примере из документации:

Затем мы можем просто вызвать этот метод в нашем приложении Flutter:

И, конечно же, пользователю будет показано новое окно подтверждения:

Проблема мультиплатформенности

Как было сказано ранее, при использовании Flutter мы можем развертывать наши приложения на нескольких платформах. Однако в предыдущем примере мы используем библиотеку js для запуска кода JavaScript, который не поддерживается ни iOS, ни Android.

Если мы попытаемся запустить наше приложение на устройстве iOS, мы получим следующую ошибку компиляции:

Это означает, что при компиляции для других платформ мы не можем использовать код, содержащий ссылки на JavaScript или dart:html. Один из способов обойти это - использовать условный импорт, о котором вы можете узнать больше в статье Антонелло Галипо: Условный импорт через Flutter и Web.

Мы начинаем с создания класса stub, в котором мы объявляем все методы, которые будем использовать, в данном случае метод showConfirm:

Затем у нас может быть один файл для каждой платформы, в котором мы указываем реализацию метода showConfirm. Для нашей цели мы собираемся оставить это как есть, поскольку это позволяет нам скомпилировать наше приложение для всех платформ, при необходимости мы укажем каждую реализацию в будущем.

Наконец, мы меняем наш простой импорт на условный импорт в верхней части файла, где мы используем метод showConfirm:

Таким образом, это импортирует stub_bundler.dart по умолчанию, однако, если он проверяет, что платформа может получить доступ к dart.library.js (например, во Flutter Web), он импортирует второй файл, в котором у нас есть наш код для отображения окна подтверждения JavaScript. Это позволит нам скомпилировать наш код для любой платформы с единственной оговоркой: когда мы вызываем эту функцию на любой платформе, кроме сети, мы получим UnimplementedError.

Добавление пользовательских библиотек JavaScript

При разработке наших приложений может возникнуть необходимость добавить особую функцию, которая уже есть в платформе, для которой мы разрабатываем, например, Google Maps.

Библиотеки Google Maps широко доступны для Android, iOS и Интернета, и вместо того, чтобы воссоздавать их во Flutter, подход Google заключался в том, чтобы использовать эти собственные библиотеки и отображать их во Flutter (вы можете увидеть реализацию в репозитории officialgoogle_maps_ flutter GitHub).

В случае с Интернетом у нас нет Pod файла или gradle для добавления наших зависимостей, поэтому мы должны добавить их в нашу папку web, либо напрямую объявив их в index.html файле, либо добавив новый .js файл в структуру.

Чтобы продемонстрировать это, мы собираемся добавить более простую библиотеку - Voca, которая позволяет нам манипулировать нашими строками, как преобразование текста в регистр верблюда.

Мы начинаем с загрузки обычной, а не минифицированной версии файла, чтобы мы могли заглянуть в имена классов и комментарии. Затем мы добавляем его в наш проект в папку js внутри web.

В нашем index.html файле мы можем добавить скрипт прямо под main.dart.js скриптом:

Перед интеграцией любого другого кода мы запускаем приложение, чтобы проверить наличие ошибок, и хотя приложение работает, Chrome Dev Tools (F12) показывает длинное сообщение об ошибке, которое начинается с:

Uncaught Error: Mismatched anonymous define()

Быстрый поиск в Google покажет веб-сайт Require.js со следующей страницей: Несоответствующие анонимные модули define ()…. При дальнейшем осмотре мы видим, что Require.js - это библиотека JavaScript, которая позволяет нам загружать новые модули в наше веб-приложение, используя определенные функции, такие как define, который voca.js использует, что означает, что нам нужно будет добавить его и в наше приложение.

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

Первое, что нам нужно сделать, это изменить структуру кода в соответствии со стандартами Require.js:

Для require.js нам просто нужно скачать последний двоичный файл из раздела Скачать на сайте и добавить его в папку js. В случае voca.js мы просто переместили файл из корневой папки в папку scripts/.

Однако в файле main.js мы укажем все зависимости JavaScript, которые требуются нашему проекту. Чтобы сделать этот модуль доступным в Dart, мы добавляем их в window:

Как и раньше, мы должны добавить эту новую зависимость в наш index.html файл и удалить voca.js зависимость. Поскольку мы используем require.js, нам нужно не только добавить его, но и добавить зависимость к main.js, что объясняется в этом ответе StackOverflow.

Читая документацию для voca.js, мы видим, что нам потребуется доступ к v для вызова каждой функции. Таким образом, мы создадим @JS() класс в Dart и укажем каждый static метод, который мы хотим использовать:

Чтобы вызвать этот метод, нам просто нужно вызвать:

Наконец, поскольку мы хотим сделать наш проект компилируемым для всех платформ, мы создаем новый метод с именем toCamelCase, в котором напрямую вызываем V.camelCase. Это означает, что как нашему stub файлу, так и оставшимся реализациям для мобильных и настольных компьютеров нужно будет просто создать метод с именем toCamelCase вместо определенного класса и статического метода:

В нашем приложении Flutter мы сможем вызывать эту функцию напрямую:

Заключение

Как и в случае с Flutter для мобильных устройств, всегда можно связаться с базовой платформой для доступа к собственному API или конкретной библиотеке. Для Flutter Web это будет проще, поскольку нам не нужно использовать PlatformChannel, и вместо этого мы можем вызывать код (почти) напрямую с помощью библиотеки js.

К счастью, команда Dart уже предоставила нам множество готовых методов для наиболее распространенных операций с помощью переменной window 🥳

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

Вы можете найти репозиторий для этой статьи здесь:



Https://www.twitter.com/FlutterComm