7.5 KiB
FFI Plugin package
Специализированный пакет Dart, содержащий API, написанный на Dart, с одной или несколькими реализациями для конкретной платформы, использующими Dart FFI.
Для демонстрации создания платформо-зависимого плагина для ОС Аврора типа "FFI Plugin package" был написан и опубликован проект "Demo Dart Packages" которые подробно описан в статье "Flutter на ОС Аврора".
Проект "Demo Dart Packages" содержит в себе пакет реализующий платформо-зависимый плагин для ОС Аврора типа "FFI Plugin package". Данный пакет использует API ОС Аврора - "Device Info API". Плагин использует QtDBus но не является типом плагина "Qt plugin package" так как не использует сигналы и слоты.
Создать пакет "FFI Plugin package" можно командой:
$ flutter-aurora create --template=plugin_ffi <package-name>
Рассмотрим плагин "ffi_plugin_device" из проекта Demo Dart Packages. ffi_plugin_device - платформо-зависимая реализация плагина "Flutter Device" для ОС Аврора типа "FFI Plugin package".
Структура пакета ffi_plugin_device
└── aurora
└── ffi_plugin_device
├── aurora
│ └── CMakeLists.txt
├── ffigen.yaml
├── lib
│ ├── ffi_plugin_device_bindings_generated.dart
│ └── ffi_plugin_device.dart
├── pubspec.yaml
└── src
├── CMakeLists.txt
├── ffi_plugin_device.cpp
└── ffi_plugin_device.h
Для генерации привязок FFI можно использовать плагин ffigen
. Пакет, предоставляющий утилиты для работы с кодом интерфейса внешних функций, называется ffi
. В pubspec.yaml
плагина нужно добавить зависимости и активировать ffiPlugin
для платформы ОС Аврора.
dependencies:
ffi: ^2.0.2
dev_dependencies:
ffigen: ^7.2.7
flutter:
plugin:
platforms:
aurora:
ffiPlugin: true
dartPluginClass: FFIPluginDevice
Для генерации привязок нужно добавить файл ffigen.yaml
в корень плагина.
Файл packages/aurora/ffi_plugin_device/ffigen.yaml
name: PluginDeviceBindings
llvm-path:
- '/usr/lib/llvm-14/lib/libclang.so' # Ubuntu 22.04
- '/usr/lib/llvm-15/lib/libclang.so' # Ubuntu 23.04
description: |
Bindings for `src/ffi_plugin_device.h`.
output: 'lib/ffi_plugin_device_bindings_generated.dart'
headers:
entry-points:
- 'src/ffi_plugin_device.h'
include-directives:
- 'src/ffi_plugin_device.h'
comments:
style: any
length: full
В файле ffigen.yaml
указываются хедеры нужных библиотек. В данном случае библиотека лежит в папке src
, которая получает название модели устройства с помощью Qt D-Bus
.
Файл packages/aurora/ffi_plugin_device/src/ffi_plugin_device.h
#ifdef __cplusplus
extern "C" {
#endif
char *getDeviceName();
#ifdef __cplusplus
}
#endif
Файл packages/aurora/ffi_plugin_device/src/ffi_plugin_device.cpp
#include <QStandardPaths>
#include <QtDBus/QtDBus>
#include "ffi_plugin_device.h"
char *getDeviceName()
{
QString deviceName = "";
if (QDBusConnection::sessionBus().isConnected()) {
QDBusInterface iface("ru.omp.deviceinfo",
"/ru/omp/deviceinfo/Features",
"",
QDBusConnection::sessionBus()
);
if (iface.isValid()) {
QDBusReply<QString> reply = iface.call("getDeviceModel");
if (reply.isValid()) {
deviceName = reply.value();
}
}
}
return deviceName.toUtf8().data();
}
Выполнить генерацию FFI-привязок можно следующей командой, выполнив её из корня плагина (например, packages/aurora/ffi_plugin_device
):
flutter-aurora pub run ffigen --config ffigen.yaml
В папке lib
будет сгенерирован файл ffi_plugin_device_bindings_generated.dart
.
Файл packages/aurora/ffi_plugin_device/lib/ffi_plugin_device_bindings_generated.dart
import 'dart:ffi' as ffi;
/// Bindings for `src/ffi_plugin_device.h`.
class PluginDeviceBindings {
/// Holds the symbol lookup function.
final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
_lookup;
/// The symbols are looked up in [dynamicLibrary].
PluginDeviceBindings(ffi.DynamicLibrary dynamicLibrary)
: _lookup = dynamicLibrary.lookup;
/// The symbols are looked up with [lookup].
PluginDeviceBindings.fromLookup(
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
lookup)
: _lookup = lookup;
ffi.Pointer<ffi.Char> getDeviceName() {
return _getDeviceName();
}
late final _getDeviceNamePtr =
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(
'getDeviceName');
late final _getDeviceName =
_getDeviceNamePtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
}
Теперь можно создать класс FFIPluginDevice
, реализующий интерфейс device_platform_interface.
Файл packages/aurora/ffi_plugin_device/lib/ffi_plugin_device.dart
class FFIPluginDevice extends DevicePlatform {
/// Метод, который выполнится при старте приложения
/// В этом методе можно установить платформо-зависимый плагин
static void registerWith() {
DevicePlatform.instance = FFIPluginDevice();
}
/// Привязки к нативным функциям в [_dylib].
final PluginDeviceBindings _bindings = PluginDeviceBindings(
DynamicLibrary.open('libffi_plugin_device.so'),
);
/// Реализация метода интерфейса [DevicePlatform]
@override
Future<String?> get deviceName async {
// Получение deviceName
final deviceName = _bindings.getDeviceName().cast<Utf8>().toDartString();
// Возврат результата
return deviceName == '' ? null : deviceName;
}
}