Кроссплатформенная разработка мобильного приложения на Flutter
Flutter 3.x на основе Dart компилирует код напрямую в нативный ARM-bytecode через AOT-компиляцию — ни JS bridge, ни WebView. Это принципиальное отличие от React Native, где любой вызов нативного API проходит через асинхронный мост. На практике разница ощутима там, где важна анимация, кастомный рендеринг и работа с жестами: Flutter рисует каждый кадр через собственный движок Skia (или Impeller с Flutter 3.10+), не полагаясь на нативные виджеты платформы.
Если проект требует 60/120fps на Flutter 3.16+ с Impeller на iOS, отдельного разговора с командой не избежать: Impeller по умолчанию включён на iOS, на Android — флагом --enable-impeller. Там, где старый Skia справлялся, Impeller может вести себя иначе при первой компиляции шейдеров.
Где Flutter выигрывает, а где требует осторожности
Кроссплатформенный подход оправдан, когда UI-логика одинаковая на iOS и Android, а продукт нужен быстро. Flutter закрывает 80–90% потребностей через стандартные виджеты Material и Cupertino. Проблемы начинаются на краях.
Платформозависимые API. Bluetooth Low Energy — через flutter_blue_plus, но его поведение на Android 12+ (когда Google сменила разрешения с ACCESS_FINE_LOCATION на BLUETOOTH_SCAN) требует отдельной обработки. На iOS — отдельный ключ NSBluetoothAlwaysUsageDescription в Info.plist плюс фоновый режим bluetooth-central. Пропустишь — App Store отклонит по guideline 5.1.1.
Background execution. Dart Isolates не решают проблему фоновых задач на уровне ОС. Для фоновой геолокации — background_locator_2 + WorkManager на Android (через workmanager package). На iOS — CLLocationManager в режиме allowsBackgroundLocationUpdates. Здесь Flutter даёт лишь тонкую обёртку, реальная логика — нативная.
Platform channels. Когда готового плагина нет, пишем MethodChannel или EventChannel. Типичный кейс — интеграция с POS-терминалом через SDK производителя (Ingenico, PAX): SDK доступен только в виде .aar для Android и .framework для iOS, оборачиваем в нативный код, пробрасываем через канал. Здесь «кроссплатформенность» заканчивается ровно на границе channel.
Как строим архитектуру
Для проектов от 5 экранов используем чистую архитектуру с разделением на data, domain, presentation слои. State management — Riverpod 2.x (провайдеры типа AsyncNotifierProvider вместо устаревшего ChangeNotifierProvider). BLoC применяем там, где команда уже с ним работала или требуется строгий event-driven flow с тестируемостью каждого состояния.
Навигация — go_router 13.x с поддержкой deep links и web-маршрутизацией (если Flutter Web нужен в проекте). Старый Navigator 1.0 избегаем — при сложных вложенных маршрутах стек ломается непредсказуемо.
DI — через get_it + injectable. Генерация кода через build_runner сокращает бойлерплейт, но требует дисциплины: freezed для immutable моделей, json_serializable для сериализации, drift или isar для локальной БД. isar в 3.x показывает ощутимо быстрее hive на операциях с коллекциями — в одном проекте с 200k записей разница составила ~3x на фильтрации.
Пример из практики. Приложение агрегатора такси — три пользовательских роли (пассажир, водитель, диспетчер), real-time обновления через WebSocket (web_socket_channel), карты через flutter_map + OpenStreetMap тайлы (клиент не хотел платить за Google Maps SDK). Фоновые обновления геопозиции водителя — через WorkManager на Android и BGAppRefreshTask на iOS с оберткой в Dart. Сборка релиза — Fastlane + GitHub Actions: один workflow собирает --release APK и IPA параллельно, подписывает, загружает в Firebase App Distribution для QA.
Тестирование и CI
Unit-тесты Dart — стандартный flutter_test. Integration-тесты — integration_test package (запуск на реальном устройстве или эмуляторе через flutter drive). Widget-тесты покрывают ключевые компоненты: testWidgets с WidgetTester и pump/pumpAndSettle.
В CI (GitHub Actions / GitLab CI) разделяем: flutter analyze + dart format --set-exit-if-changed на каждый PR, тесты — параллельно на Android API 33 emulator и iOS Simulator. Сборки под TestFlight и Google Play Internal Track — через Fastlane Match для сертификатов.
Производительность профилируем в flutter run --profile + DevTools: Timeline view показывает jank-фреймы (>16ms), Memory view — heap между rebuild-ами. Типичные проблемы: избыточные setState в глубоком дереве, неоптимизированные изображения (декодируем без cacheWidth/cacheHeight), дорогие вычисления в build() вместо вынесенных в compute().
Что влияет на сроки
| Масштаб проекта | Примерные сроки |
|---|---|
| MVP, 5–10 экранов, REST API | 6–10 недель |
| Средний продукт, 15–30 экранов, offline-режим | 3–6 месяцев |
| Сложный продукт: real-time, платежи, карты, BLE | 6–12 месяцев |
Стоимость рассчитывается индивидуально после анализа требований — технического задания, макетов, описания интеграций.
Типичные ошибки при самостоятельной разработке
Чаще всего видим три проблемы при аудите Flutter-проектов от других команд.
Неправильная архитектура провайдеров. ChangeNotifier с глобальным состоянием, который перестраивает весь экран на любое изменение. Переход на Riverpod с гранулярными select-ами снимает проблему, но требует рефакторинга.
Platform channel без error handling. Нативный код бросает исключение → канал падает → приложение крэшится без понятного сообщения. Все вызовы через MethodChannel оборачиваем в try/catch PlatformException.
Ignoring isolates для тяжёлых операций. JSON десериализация 10MB ответа на main isolate даёт заметный фриз. compute() или ручной Isolate.spawn — обязательно для данных >1MB.







