[Из песочницы] Flutter и десктоп разработка
Идея писать мультиплатформенные приложения уже далеко не нова. Flutter так же предоставляет возможность это делать. В этой статье я постараюсь описать два подхода запуска мобильного приложения на десктопе, которые я сам использую для разработки мобильных приложений. Я перестал запускать эмулятор и симулятор во многом потому, что появилась возможность обойтись без них. Тем, кому интересна идея, добро пожаловать под кат.
Итак, зачем вообще писать десктоп версию минуя эмуляторы и симуляторы?
Во-первых, ресурсы рабочей станции. В моем текущем коммерческом проекте используется очень много анимаций и различных «тяжелых» виджетов. На моем весьма неплохом ноутбуке с i7, 16ГБ оперативки и видеокартой GTX1050Ti процессор на весьма сложных переходах и анимациях подскакивает до 30–35% в debug режиме, оперативки эмулятор андройда «кушает» до 2ГБ. Кадры, естественно, теряются и анимации движутся «рывками», что не дает нормально оценить полноту картины. При запуске десктоп версии, мой процессор «отдыхает» и нагружается при тех же анимациях до 3–4% и это, на минуточку, экономия процессора в 8–10 раз и оперативки 300–400МБ.
Во-вторых, возможность быстро просмотреть правильность расстановки виджетов на различных девайсах. Просто взять окошко и растянуть, либо сжать до минимальных размеров и это громадная экономия своего личного времени и времени коллег-тестировщиков.
Используя, теперь уже часть Flutter — flutter-desktop-embedding
Здесь все просто. Копируем папки windows, linux, macos к себе в рабочий проект. Прописываем в консоли команду разрешения использовать библиотеки ОС:
flutter config --enable-windows-desktop
flutter config --enable-linux-desktop
flutter config --enable-macos-desktop
Все. Теперь у нас при выборе девайса появляется, в зависимости от платформы, эмулятор десктоп версии с названием Linux, Macos, Windows. Выбираем, запускаем, работаем.
Получаем идентичную эмулятору платформу с Hot Reload и Hot Restart.
Плюсы:
- самые необходимые плагины, например, file_chooser (выбор папок в системе), window_size (изменение размеров окна) уже есть
- все работает «из коробки»
- компиляция release сборки
Минусы:
- нет нормального обработчика хоткеев, например, CTRL+C, CTRL+V
- расширения и плагины пишутся раздельно для каждой платформы, в своих подпапках windows, linux, macos
Используя неофициальный плагин go-flutter-desktop, который предлагает для разработки всю мощь языка Go
Для его установки потребуется hover и библиотека рендеринга glfw. После того как установили все нужные нам зависимости, находим любой пример работы, например, keyboard_event, копируем целиком папку Go и в корне нашей программы пишем:
hover run. Команда все сделает за нас — установит нужные зависимости и запустит десктоп версию.
Получаем так же потребляющую мало ресурсов версию приложения для win, mac, linux и так же с хот релоадом и хот рестартом, вдобавок уже с работающим обработчиком CTRL+C, CTRL+V.
Основные плюсы:
- возможность писать плагины на Go для коммуникации Flutter и десктоп части
- одна кодовая база под все платформы
- огромное количество модулей на Go, а если модулей нет, всегда можно написать самому и это будет работать — запуск командной строки, нативная нотификация, выгрузка логов в файл, запуск нативного приложения и т.д.
- библиотека glfw уже предлагает множество функций для работы с программой, например, изменение, ограничение размеров окна
- основные нужные плагины уже реализованы, file_chooser (выбор файла), path_provider (получение темповой директории, директории для загрузок), video_player, который использует ffmpeg для проигрывания видео
Минусы:
- не все плагины нужные есть. Например, нет (на момент написания статьи) плагина для сворачивания приложения в трей. Так же у меня не получилось запустить плагин video_player. В консоли вываливается ошибка «нет обработчика нужного канала». Похоже, флаттеровский плагин поменял API и go-версия не успела обновиться
- для совместного использования плагина и, например, VSCode требуется совершить еще дополнительные телодвижения, т.к. hover запускает приложение из консоли и нужно аттачиться к процессу
{
"name": "go-flutter_desktop",
"request": "attach",
"deviceId": "flutter-tester",
"observatoryUri": "http://127.0.0.1:50300/",// "${command:dart.promptForVmService}",
"type": "dart",
"program": "lib/desktop_main.dart" // Dart-Code v3.3.0 required
}
и потребуется установить плагин для удобства в VSCode — Hover
Пример плагина для эмуляции размеров окна эмулятора
Проще всего скопировать папку Go из репозитория примеров проекта, например, из github.com/go-flutter-desktop/examples/tree/master/text_demo
Далее в директории Go создаем папки plugins/desktop_utils и в ней файл с нашим будущим плагином, назовем его main.go и впишем в него
package desktop_utils
import (
"github.com/go-flutter-desktop/go-flutter"
"github.com/go-flutter-desktop/go-flutter/plugin"
"github.com/go-gl/glfw/v3.3/glfw"
)
const channelName = "go-test/desktop_utils"
type DesktopUtilsFlutterPlugin struct {
window *glfw.Window
channel *plugin.MethodChannel
}
var _ flutter.Plugin = &DesktopUtilsFlutterPlugin{}
var _ flutter.PluginGLFW = &DesktopUtilsFlutterPlugin{}
func (p *DesktopUtilsFlutterPlugin) InitPlugin(messenger plugin.BinaryMessenger) error {
channel := plugin.NewMethodChannel(messenger, channelName, plugin.StandardMethodCodec{})
channel.HandleFunc("set_window_size", p.setWindowSizeHandler)
return nil
}
func (p *DesktopUtilsFlutterPlugin) InitPluginGLFW(window *glfw.Window) error {
p.window = window
return nil
}
func (p *DesktopUtilsFlutterPlugin) setWindowSizeHandler(arguments interface{}) (reply interface{}, err error) {
argumentsMap := arguments.(map[interface{}]interface{})
var minW = int(argumentsMap["minW"].(int32))
var minH = int(argumentsMap["minH"].(int32))
var maxW = int(argumentsMap["maxW"].(int32))
var maxH = int(argumentsMap["maxH"].(int32))
p.window.SetSizeLimits(minW, minH, maxW, maxH);
return nil, nil
}
где channelName — имя нашего канала для связки с Flutter
minW, minH, maxW, maxH — минимальные/максимальные размеры окна
Инициализируем наш файл как Go-модуль: go mod init desktop_utils (появится файл go.mod).
Идем в директорию Go, находим файлик go.mod, дописываем локальную директорию нашего плагина: replace desktop_utils => ./plugins/desktop_utils
и добавляем наш плагин в options.go в строчку опций:
var options = []flutter.Option{
flutter.WindowInitialDimensions(InitW, InitH),
flutter.AddPlugin(&desktop_utils.DesktopUtilsFlutterPlugin{}),
}
На этом Go-часть закончена.
Flutter часть максимально проста: нам нужно только создать канал (MethodChannel) с нашим названием: go-test/desktop_utils и передать в него нужные нам параметры длины и ширины:
const MethodChannel _utilsChannel = MethodChannel('go-test/desktop_utils');
_utilsChannel.invokeMethod(
'set_window_size',
{
'minW': 400,
'minH': 600,
'maxW': 800,
'maxH': 1200,
},
);
На этом все. Наш плагин готов.
Пример можно посмотреть у меня в репозитории.
Можно использовать плагин flutter-rs и язык Rust.
К сожалению, мною не изучен, но упомянуть его как еще одну возможность запуска десктоп версии я обязан.
Flutter — уникальный инструмент, открывающий возможности быстрой мультиплатформенной разработки.Я считаю, что у технологии большое будущее. С каждым днем людей, вовлеченных в разработку на Flutter становится все больше и больше.