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

Сборка, flavors и публикация в сторы

Режимы сборки, flavors, dart-define, подпись, обфускация и релиз в сторы.

Собрать APK на своей машине умеет каждый. На собесе middle и senior проверяют другое: понимаете ли вы режимы компиляции, как развести окружения, как подписывается приложение и как релиз попадает в магазин без ручной работы.

Режимы сборки

РежимКомпиляцияДля чего
debugJIT, ассерты, отладчик, hot reloadразработка
profileAOT, почти как релиз, но с инструментами профилированиязамеры производительности
releaseAOT, без ассертов и отладки, минимальный размерпользователи

Hot reload подменяет код в работающей Dart VM и перестраивает дерево, сохраняя состояние. Он не перезапускает main и initState, поэтому изменения в них или в глобальных инициализаторах требуют hot restart (перезапуск Dart-кода с потерей состояния). Изменения в нативном коде и в pubspec.yaml с плагинами требуют полной пересборки. В release hot reload нет: код скомпилирован AOT в машинный.

Окружения: dart-define и flavors

Адрес API, ключи аналитики и флаги разводят по окружениям, а не правят перед сборкой руками.

flutter run --dart-define=API_URL=https://dev.api.example
flutter build appbundle --dart-define-from-file=env/prod.json
const apiUrl = String.fromEnvironment('API_URL', defaultValue: 'http://localhost');

Значения подставляются при компиляции. Это не способ спрятать секрет: строки легко достать из бинарника. Настоящие секреты живут на сервере.

Flavors создают разные варианты приложения: свой applicationId/bundle id, имя, иконка, Firebase-конфиг. На Android это productFlavors в Gradle, на iOS схемы и конфигурации Xcode. Запуск: flutter run --flavor dev -t lib/main_dev.dart. Так dev и prod сборки стоят на одном телефоне рядом.

Версия

В pubspec.yaml строка version: 1.4.0+27: до плюса видимая версия (versionName, CFBundleShortVersionString), после плюса номер сборки (versionCode, CFBundleVersion). Номер сборки должен расти с каждой загрузкой в магазин, иначе стор её отклонит.

Android

  • В Google Play загружают App Bundle (flutter build appbundle), а не APK: магазин сам собирает APK под архитектуру и плотность экрана устройства.
  • Подпись: свой ключ загрузки (upload key) в keystore, настройки в key.properties, который не коммитят. С Play App Signing Google хранит ключ подписи приложения, а потерянный ключ загрузки можно сбросить через поддержку.
  • R8 сжимает и обфусцирует нативную часть. Если плагин использует рефлексию, могут понадобиться правила ProGuard.

iOS

  • Нужен Mac с Xcode, аккаунт Apple Developer, сертификат и provisioning profile.
  • flutter build ipa собирает архив для загрузки в App Store Connect, оттуда сборка идёт в TestFlight и на ревью.
  • Права (камера, геолокация) требуют описаний в Info.plist, иначе приложение упадёт при запросе или не пройдёт ревью.

Обфускация и символы

flutter build apk --obfuscate --split-debug-info=build/symbols переименовывает символы Dart и выносит отладочную информацию в отдельные файлы. Эти файлы нужно сохранить для каждой версии: без них стек-трейсы из Crashlytics или Sentry не расшифровать (flutter symbolize).

CI/CD

Типичный конвейер: анализатор (flutter analyze), тесты, сборка с номером из CI, подпись секретами из хранилища CI, выгрузка в Firebase App Distribution или TestFlight, затем в стор. Инструменты: GitHub Actions, GitLab CI, Codemagic, fastlane для загрузки в магазины.

Обновлять Dart-код в обход стора, как OTA в React Native, во Flutter штатно нельзя: AOT-код. Есть сторонние решения (например, Shorebird), но и они должны соблюдать правила магазинов.

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

  • Debug, profile, release: чем отличаются и почему нельзя мерить в debug.
  • Hot reload против hot restart: что не подхватит hot reload.
  • Как развести dev и prod: dart-define, flavors.
  • Как подписывается Android-приложение, зачем App Bundle.
  • Зачем --split-debug-info и что будет, если потерять символы.

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

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