Урок по треку Flutter и Dart 6 мин

Платформенные каналы, Pigeon и FFI

MethodChannel, EventChannel, Pigeon, FFI и плагины.

Flutter рисует интерфейс сам, но камера, биометрия, платежи и датчики живут в нативном коде Android (Kotlin/Java) и iOS (Swift/Objective-C). Связь с ними строится через платформенные каналы, генератор Pigeon или прямой вызов C через FFI.

MethodChannel: вызов метода

Асинхронный вызов «запрос-ответ» по имени канала.

const _channel = MethodChannel('app.example/battery');

Future<int?> batteryLevel() async {
  try {
    return await _channel.invokeMethod<int>('getLevel');
  } on PlatformException catch (e) {
    log('ошибка платформы: ${e.code} ${e.message}');
    return null;
  } on MissingPluginException {
    return null; // нет реализации на этой платформе
  }
}

На Android в MainActivity (или в плагине) регистрируется обработчик с тем же именем канала, он вызывает result.success(value), result.error(code, message, details) или result.notImplemented(). На iOS то же через FlutterMethodChannel в AppDelegate.

Что важно знать:

  • Вызовы асинхронные и сериализуются стандартным кодеком (StandardMessageCodec): null, bool, числа, String, Uint8List и типизированные списки, List, Map. Свои классы надо переводить в эти типы.
  • Обработчики на нативной стороне по умолчанию выполняются в главном потоке платформы. Долгая работа там тормозит UI приложения: её уводят в фоновый поток, а result вызывают, когда работа готова. Сообщения во Flutter отправляют с главного потока платформы. Есть и встроенный вариант: обработчик канала можно привязать к фоновой очереди задач (TaskQueue).
  • Имена каналов и методов это строки: опечатка проявится только в рантайме как MissingPluginException или notImplemented.

EventChannel и BasicMessageChannel

  • EventChannel даёт Stream событий с платформы: геолокация, датчики, состояние сети. Подписка на стороне Dart вызывает onListen у нативного StreamHandler, отмена вызывает onCancel, где нужно отписаться от источника.
  • BasicMessageChannel шлёт сообщения в обе стороны со своим кодеком, когда модель «метод и результат» не подходит.

Нативная сторона тоже может вызвать метод в Dart через тот же MethodChannel (setMethodCallHandler в Dart).

Pigeon: типобезопасные каналы

Pigeon генерирует код каналов для Dart, Kotlin, Swift по описанию API на Dart:

@HostApi()
abstract class BatteryApi {
  int getLevel();
}

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

FFI

dart:ffi вызывает функции C напрямую, без сериализации и синхронно. Подходит для библиотек на C, C++ и Rust: криптография, обработка изображений, SQLite. Привязки генерирует ffigen. Для Java/Kotlin и Objective-C/Swift есть генераторы jnigen и ffigen с поддержкой Objective-C, но это более новые и менее устоявшиеся инструменты.

Плагины и платформенные виджеты

Код с каналами упаковывают в плагин: Dart-API плюс реализации под платформы. Federated plugins разносят платформы по отдельным пакетам. Нативный экран (карта, веб-вью) встраивается через Platform Views (AndroidView, UiKitView): это дорого по производительности, используйте их точечно.

Частые ошибки

  • Тяжёлая работа в обработчике на главном потоке платформы.
  • Не обработан PlatformException и MissingPluginException.
  • Не отписались от нативного источника в onCancel: утечка и расход батареи.
  • Передача своих объектов без перевода в поддерживаемые кодеком типы.

Что спросят на собесе

  • Как вызвать нативный код из Flutter, какие бывают каналы.
  • Какие типы можно передать через канал.
  • Зачем Pigeon и чем он лучше ручных каналов.
  • Когда выбирать FFI.
  • Что такое Platform Views и почему они дорогие.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.