Кроссплатформенная разработка: что это, подходы и как выбрать

Кроссплатформенная разработка: что это, подходы и как выбрать Полезное

Кроссплатформенная разработка — это создание программы, которая из одной общей кодовой базы работает на нескольких операционных системах или устройствах: Android и iOS, Windows, macOS и Linux, браузере. Общей обычно бывает не вся программа, а ее значимая часть — логика, иногда и интерфейс; платформенные детали все равно пишут отдельно.

Ниже — словарь терминов, устройство основных подходов, их сравнение и порядок выбора под задачу.

Словарь: четыре термина, которые путают

Термин Что значит Пример
Нативная разработка Отдельное приложение под каждую ОС на ее «родном» стеке Swift и SwiftUI для iOS, Kotlin и Jetpack Compose для Android
Кроссплатформенная разработка Одна кодовая база, сборка под несколько ОС Flutter, React Native, Kotlin Multiplatform, .NET MAUI
Гибридное приложение Веб-интерфейс (HTML, CSS, JavaScript) внутри нативной оболочки с WebView Ionic с Capacitor, Apache Cordova
Кроссплей в играх Совместная игра или общий прогресс для игроков на разных платформах Матч, где игроки с ПК и консоли в одном лобби

Гибридный подход — частный случай кроссплатформенного: общий код есть, но интерфейс рисует встроенный браузерный движок. А в играх различают мультиплатформенность (игра выпущена на нескольких платформах) и кроссплей (игроки разных платформ играют вместе). Игра может быть мультиплатформенной без кроссплея.

Как код попадает на разные платформы

Главное различие подходов — что именно рисует интерфейс и во что превращается общий код. Упрощенная модель (детали зависят от версии фреймворка и платформы):

  • Flutter. Код на Dart в релизной сборке компилируется заранее (AOT) в машинный код. Интерфейс фреймворк рисует сам своим движком, а не собирает из системных виджетов, поэтому выглядит одинаково на всех ОС.
  • React Native. Код на JavaScript или TypeScript исполняется движком (по умолчанию Hermes), а интерфейс состоит из настоящих нативных компонентов платформы.
  • Kotlin Multiplatform (KMP). Общая часть на Kotlin компилируется в байт-код JVM для Android и в нативный код для iOS. Интерфейс можно оставить нативным на каждой ОС или сделать общим на Compose Multiplatform.
  • Electron. Настольное приложение — это веб-страница и Node.js внутри поставляемого вместе с программой движка Chromium. Так сделаны, например, Visual Studio Code и Slack.
  • Веб и PWA. Приложение работает в браузере, установка не нужна; доступ к возможностям устройства ограничен тем, что разрешает браузер.

Языки с виртуальной машиной (Java, C#) и интерпретатором (Python) переносимы, пока на целевой ОС есть их среда выполнения; в играх ту же роль играют движки Unity и Unreal Engine.

Сравнение подходов

Подход Язык Кто рисует интерфейс Платформы Когда брать Ограничение
Нативный Swift, Kotlin, C# и др. Система Одна на кодовую базу Максимум возможностей ОС, сложная графика, строгие требования к отзывчивости Две и больше команды и кодовые базы
Flutter Dart Собственный движок Android, iOS, веб, Windows, macOS, Linux Одинаковый дизайн везде, один интерфейс на все экраны Новые API ОС — через плагины или свой платформенный код
React Native JavaScript, TypeScript Нативные компоненты Android, iOS (другие — через сторонние проекты) Команда знает React и веб-стек Поведение компонентов может различаться между ОС
Kotlin Multiplatform Kotlin Нативный или Compose Multiplatform Android, iOS, десктоп, веб, сервер Общая бизнес-логика при нативном интерфейсе Экосистема библиотек моложе, чем у нативного Android
.NET MAUI C# Нативные элементы через абстракцию Android, iOS, Windows, macOS Команда на .NET Linux официально не поддерживается
Electron JavaScript, HTML, CSS Chromium Windows, macOS, Linux Десктоп силами веб-команды Большой размер дистрибутива и расход памяти
Веб, PWA JavaScript, TypeScript Браузер Любое устройство с браузером Контент, формы, кабинеты Ограниченный доступ к устройству, особенно на iOS

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

Одна кодовая база — не значит «платформ не видно»

Даже в кроссплатформенном проекте часть кода зависит от ОС: пути к файлам, место для настроек, разрешения, уведомления. Минимальный пример на Dart (язык Flutter) — консольная программа выбирает каталог настроек по соглашениям разных ОС:

import 'dart:io';

// Где хранить настройки приложения: у каждой ОС свое соглашение.
String configDir(String app) {
  final env = Platform.environment;
  if (Platform.isWindows) {
    return '${env['APPDATA']}\\$app';
  }
  if (Platform.isMacOS) {
    return '${env['HOME']}/Library/Application Support/$app';
  }
  // Linux и другие Unix: стандарт XDG, по умолчанию ~/.config
  final xdg = env['XDG_CONFIG_HOME'];
  final base = (xdg != null && xdg.isNotEmpty) ? xdg : '${env['HOME']}/.config';
  return '$base/$app';
}

void main() {
  print('ОС: ${Platform.operatingSystem}');
  print('Разделитель путей: ${Platform.pathSeparator}');
  print('Настройки: ${configDir('notes')}');
}

Запуск dart run main.dart на Dart 3.13 в Linux-контейнере под пользователем root печатает:

ОС: linux
Разделитель путей: /
Настройки: /root/.config/notes

Если задать переменную XDG_CONFIG_HOME=/data/cfg, последняя строка станет Настройки: /data/cfg/notes. На macOS и Windows сработают другие ветки того же файла. Это и есть кроссплатформенный код: один исходник, а платформенные различия собраны в одном месте, а не разбросаны по программе.

Во Flutter для таких путей обычно берут готовый плагин (например, path_provider), который учитывает правила каждой ОС, включая песочницу iOS и Android. Пример выше показывает принцип, а не замену плагину.

Типичная ошибка: путь, собранный под одну ОС

Код, написанный и проверенный только на Windows, часто склеивает пути обратной косой чертой:

import 'dart:io';

void main() {
  Directory('data').createSync();
  File('data\\notes.txt').writeAsStringSync('привет');
  for (final e in Directory('.').listSync()) {
    print(e.path);
  }
}

Результат в Linux — ошибки нет, но файл создан не в папке data, а рядом с ней, с обратной косой чертой прямо в имени:

./main.dart
./data\notes.txt
./data

Исправление — не склеивать разделитель вручную: взять Platform.pathSeparator или функцию join из пакета path:

import 'dart:io';

void main() {
  final sep = Platform.pathSeparator;
  Directory('data').createSync();
  File('data${sep}notes.txt').writeAsStringSync('привет');
  for (final e in Directory('data').listSync()) {
    print(e.path);
  }
}

Теперь файл лежит в нужной папке: программа печатает data/notes.txt. Такие ошибки не видны на машине разработчика, поэтому проект тестируют на каждой целевой ОС, обычно в CI.

Плюсы и минусы: без мифов

Что дает общий код. Одна реализация логики вместо двух-трех, общие исправления ошибок, одна команда. Выигрыш тем больше, чем больше доля общего кода.

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

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

Интерфейс. Flutter рисует одинаково везде, и это удобно для бренд-дизайна, но элементы могут отличаться от привычных системных. React Native и MAUI используют нативные элементы, зато их поведение иногда расходится между ОС.

Безопасность. Кроссплатформенность сама по себе не делает приложение уязвимее, но добавляет слой, который надо обновлять. Electron-приложение содержит свою копию Chromium: пока разработчик не выпустит версию на обновленном Electron, у пользователей остаются уязвимости, уже исправленные в Chrome. Для Electron отдельно важно держать включенными изоляцию контекста (contextIsolation) и не открывать Node.js удаленному контенту (nodeIntegration выключен). В мобильных фреймворках риск чаще в сторонних плагинах — их версии и права проверяют так же, как любую зависимость.

Что все равно делают под каждую платформу. Подпись и публикация в магазинах, разрешения, push-уведомления, встроенные покупки. Для сборки под iOS в любом фреймворке нужен Mac с Xcode.

Как выбрать подход

  1. Определите платформы. Только мобильные, только десктоп или все сразу — это отсекает половину вариантов из таблицы.
  2. Посмотрите на команду. Веб-разработчикам ближе React Native и Electron, Android-команде — KMP, .NET-команде — MAUI; Flutter потребует изучить Dart, но язык осваивается быстро, если знаете Java, Kotlin или TypeScript.
  3. Выпишите зависимости от устройства. Камера, Bluetooth, фоновая работа, платежи. Для каждой проверьте, есть ли поддерживаемый плагин.
  4. Решите, что важнее в интерфейсе: единый дизайн (Flutter) или привычные элементы каждой ОС (нативный UI, KMP с нативным UI, React Native).
  5. Соберите прототип одного сложного экрана на выбранном стеке и запустите на самом слабом целевом устройстве. Это дешевле, чем менять стек после релиза.

Если нужен предельный доступ к возможностям одной ОС, нативная разработка остается честным выбором.

Выводы

  • Кроссплатформенная разработка — одна кодовая база для нескольких ОС; обычно общими бывают логика и часть интерфейса, а платформенные детали пишут отдельно.
  • Подходы отличаются тем, кто рисует интерфейс: собственный движок (Flutter), нативные компоненты (React Native, MAUI), встроенный браузер (Electron, гибридные приложения).
  • Разница в производительности с нативом зависит от задачи: для форм и списков она часто незаметна, для тяжелой графики и старта на слабых устройствах — заметна.
  • Даже общий код содержит платформенные ветки (пути, настройки, разрешения), поэтому его тестируют на каждой целевой ОС.
  • Выбор стека определяют платформы, навыки команды и зависимости от устройства, а проверяет его прототип сложного экрана.

Где применяется / связь с практикой

Освойте тему на практике

Кроссплатформенные стеки используют в мобильных приложениях сервисов, корпоративных системах и настольных инструментах. Если хотите освоить Flutter системно — от Dart и архитектуры приложения до публикации в магазины Android и iOS, — посмотрите курс «Flutter-разработчик». Чтобы сначала познакомиться с преподавателями и форматом, загляните на бесплатные открытые уроки Otus.

FAQ

Можно ли одним проектом покрыть мобильные приложения и сайт?
Технически да: Flutter и Kotlin Multiplatform умеют собирать веб-версию, а React Native — через проект React Native for Web. Но веб-сборка часто уступает обычному сайту в скорости загрузки и индексации, поэтому публичные страницы нередко делают отдельно.

Что стало с Xamarin?
Microsoft прекратила его поддержку в мае 2024 года; преемник — .NET MAUI. Проекты на Xamarin переводят на MAUI или на другой стек.

Java — это кроссплатформенный язык?
Программа на Java переносима между ОС, где установлена JVM, но это не решает задачу мобильного интерфейса под iOS. Кроссплатформенность языка и кроссплатформенность приложения с интерфейсом — разные вещи.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)