Как использовать канал платформы во Flutter

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

В этом посте я несколько раз упомянул слово "нативный". Под «родными» я подразумеваю платформы iOS и Android.

Другой вариант использования — когда вы добавляете модуль флаттера в собственное приложение (Flutter addToApp), и в этом случае вы хотите вызвать какой-либо собственный метод для получения некоторой информации в вашем модуле флаттера, вы можете использовать канал метода.

Многие плагины Flutter внутренне используют каналы платформы.

Вот архитектурный обзор:

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

Итак, сначала мы инициализируем канал метода, как показано ниже:

static const platformChannel = MethodChannel('test.flutter.methodchannel/iOS');

Поэтому, если вы вставили это в свою IDE, вы можете получить ошибку. В ходе расследования вы обнаружите, что он не может идентифицировать MethodChannel, поэтому для использования платформы нам нужно импортировать пакет service.

Мы использовали String в инициализаторе канала метода, это имя канала метода, вы можете передать любую строку, которая вам нравится, но для стандартизации рекомендуется использовать обратную Reverse domain name notation.

Помимо обязательного параметра name, инициализатор принимает еще два параметра:

  • codec
    Если в этой области не указано значение, используется StandardMethodCodec.

«Стандартные каналы платформы используют стандартный кодек сообщений, который поддерживает эффективную двоичную сериализацию простых значений, подобных JSON, таких как логические значения, числа, строки, байтовые буферы, а также списки и карты. Сериализация и десериализация этих значений, а также сообщений происходит автоматически, когда вы отправляете и получаете значения». — Официальные документы

  • binaryMessenger(Optional)
    BinaryMessenger помогает в отправке двоичных данных на нативные платформы,
    Если мы ничего не передаем, используется defaultBinaryMessenger, если вам интересно, это определено в binding.dartfile.

Таким образом, простыми словами binaryMessenger используется для передачи двоичных данных на собственную платформу и из нее, а codec используется для сериализации и десериализации этих значений.

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

Давайте пройдемся по коду; это довольно просто.

  1. Мы вызываем invokeMethod на канале платформы.
    Этот метод принимает имя метода и список аргументов, в приведенном выше примере мы опустили список аргументов.
    Обратите внимание на имя метода, потому что у нас есть использовать его на родной платформе.
  2. Платформа может выдать какое-то исключение, поэтому в целях безопасности мы вернемся к блоку catch.
    Примечание. В случае какого-либо исключения для конкретной платформы создается исключение PlatformException.
  3. Мы делаем setState, который перестроит пользовательский интерфейс, чтобы показать модель устройства.

Мы закончили на стороне Flutter, рассмотрим реализацию на стороне iOS.

В рабочей области флаттера есть папки iOS и Android, по умолчанию они находятся на том же уровне, что и папка lib, мы будем использовать их для написания кода для конкретной платформы.

Откройте модуль iOS в Xcode и выполните следующие действия:

  • Найдите свой проект в Finder.
  • Перейдите в папку ios, если имя вашего проекта simple_platform_channel, то simple_platform_channel > ios.
  • В папке iOS откройте runner.xcworkspace

Примечание для разработчиков, не использующих iOS: Runner.xcodeproj — это файл проекта, но, поскольку шаблон проекта Flutter по умолчанию использует Cocopods в качестве менеджера зависимостей, он создает файл рабочей области, в который встраиваются зависимости.

Давайте напишем нативный метод, а стоило ли его писать?
Вот путь:

Runner > Runner > AppDelegate

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

Существует класс AppDelegate, который аннотирован @UIApplicationMain,поэтому в основном с него и начинается выполнение программы, это эквивалентно void main() во Flutter.

Теперь, если мы увидим внутри класса, есть один метод application с параметром didFinishLaunchingWithOptions, как видно из названия, этот метод выполняется сразу после запуска приложения. Мы внесем изменения в этот метод.

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

Теперь мы будем использовать PlatformChannel, который мы определили во Flutter.

Вот код. Не волнуйтесь, если это выглядит пугающе; мы рассмотрим это шаг за шагом:

Давайте разберем приведенный выше фрагмент:

  1. Мы пытаемся получить FlutterViewController, если вам интересно, откуда окно появилось на сцене, оно в основном определено в FlutterAppDelegate, которое расширено AppDelegate, а FlutterViewController установлено как rootViewController, поэтому мы пытаемся получить к нему доступ.
  2. Почему мы снова создаем канал методов?
    Поскольку мы пытаемся обеспечить взаимодействие между двумя фреймворками, соединения должны быть установлены с обеих сторон.
    Примечание:
    - Для того, чтобы установить соединение, нам нужно иметь то же имя канала, которое мы определили во Flutter.
    - binaryMessenger. Мы используем бинарный мессенджер по умолчанию, предоставленный FlutterViewController. Вот почему мы вытащили FlutterViewController.
  3. Здесь мы готовим обработчик метода. Он будет обрабатывать любой вызов метода, вызванный в канале метода.
  4. Мы присоединяем обработчик обратного вызова метода и передаем замыкание (если вам интересно, что это, это просто анонимная функция. Если вы использовали List.forEach во флаттере, то вы уже это знаете).
    Есть два параметра анонимной функции:
    - call ( FlutterMethodCall) — предоставляет подробности вызова метода, такие как имя метода и его аргументы.
    - result (FlutterResult) — это результат, который вы вернете. вернемся к модулю флаттера, он обозначается как @escaping, что означает, что это асинхронный вызов, поэтому вы можете выполнить некоторую длительную операцию, а затем вызвать результат.
  5. Просто проверьте, является ли имя вызываемого метода getDeviceModel. Здесь нет особого смысла проверять, но когда методов несколько, нужно проверять, чтобы различать их.
  6. Вызов другого метода для получения модели устройства.
  7. Мы использовали iOS device API, чтобы получить результат.
  8. Вызов result с данными, что помогает передать их обратно в модуль Flutter.
  9. Если мы вызываем какой-то метод со стороны Flutter, который не реализован в родном коде, мы бросаем FlutterMethodNotImplemented.

Просто не правда ли!

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

Как передать некоторые данные из Flutter в iOS при вызове метода?

Помните, мы обсуждали метод invokeMethod, который принимает массив динамических аргументов. Давайте посмотрим на пример:

platformChannel.invokeMethod('getDeviceModel',
    {"flutterAppVersion": "0.0.1", "developerName": "XYZ"});

Как передать данные обратно в модуль Flutter?

Мы можем передать любые данные в результате. Например:

result( ------ )

Но чтобы немного упростить ситуацию, Flutter предоставляет другие альтернативы invokeMethod:

1. Future<T?> invokeMethod<T>(String method, [ dynamic arguments ])
2. Future<Map<K, V>?> invokeMapMethod<K, V>(String method, [ dynamic arguments ])
3. Future<List<T>?> invokeListMethod<T>(String method, [ dynamic arguments ])

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

Краткое содержание

За пять простых шагов вы можете запустить код для конкретной платформы во Flutter:

  1. Создайте канал метода во Flutter.
  2. Вызовите метод на канале и дождитесь результата.
  3. Создайте FlutterMethodChannel в iOS в методе приложения с didFinishLaunchingWithOptions.
  4. Установите обработчик обратного вызова на канале.
  5. Используйте собственные API для выполнения некоторых действий и возврата результата.

Ресурсы

Несколько полезных ресурсов на канале Платформы: