Урок 22 из 25 · Месяц 6. Чат и публикация приложений
Публикация в App Store и Google Play, README и Makefile
Содержание урока
- Чек-лист перед первой сборкой
- Версии приложения
- Иконки и splash-экран
- Android: подпись приложения
- Шаг 1. Создаём ключ загрузки
- Шаг 2. Файл key.properties
- Шаг 3. Подключаем ключ в Gradle
- Play App Signing
- Google Play: внутреннее тестирование
- iOS: сборка и TestFlight
- Шаг 1. Bundle ID и подпись
- Шаг 2. Карточка в App Store Connect
- Шаг 3. Сборка и загрузка
- Шаг 4. TestFlight
- README: лицо проекта
- Makefile: команды в одно слово
- Типичные ошибки
- Практика
- Итоги
Приложение готово, работает на вашем телефоне — пора отдать его людям. Публикация в магазины пугает новичков количеством шагов: ключи подписи, иконки, версии, анкеты, тестирование. В этом уроке пройдём весь путь по порядку — от подготовки сборки до TestFlight и внутреннего тестирования в Google Play — и наведём порядок в проекте с помощью README и Makefile.
Чек-лист перед первой сборкой
Перед тем как идти в магазины, проверьте основы:
- Уникальный идентификатор:
applicationId(Android) и Bundle ID (iOS), напримерcom.yourcompany.shop. После публикации его нельзя изменить — это будет уже другое приложение. Никакихcom.example. - Название приложения —
android:labelв манифесте иCFBundleDisplayNameвInfo.plist. - Иконка и splash-экран — свои, а не стандартные Flutter.
- Версия в
pubspec.yaml. - Боевой flavor и конфиг —
prod, правильный адрес API (см. урок про сборки и flavors). - Нет отладочного мусора:
print, тестовые кнопки, открытые логи с токенами. - Политика конфиденциальности — страница в интернете. Оба магазина требуют ссылку на неё.
Версии приложения
Версия задаётся одной строкой в pubspec.yaml:
version: 1.4.2+17
| Часть | Что это | Android | iOS |
|---|---|---|---|
1.4.2 |
Версия для людей (видна в магазине) | versionName |
CFBundleShortVersionString |
17 |
Номер сборки (build number) | versionCode |
CFBundleVersion |
Версию для людей обычно ведут по семантическому версионированию MAJOR.MINOR.PATCH: PATCH — исправили баги (1.4.2 → 1.4.3), MINOR — новая функция (1.4.3 → 1.5.0), MAJOR — большие несовместимые изменения (1.5.0 → 2.0.0).
Иконки и splash-экран
Рисовать иконки всех размеров вручную не нужно — это делает пакет flutter_launcher_icons. Подготовьте квадратную картинку 1024×1024 без прозрачности (iOS не принимает прозрачные иконки).
flutter pub add dev:flutter_launcher_icons
flutter_launcher_icons:
android: true
ios: true
image_path: "assets/icon/icon.png"
remove_alpha_ios: true
adaptive_icon_background: "#FFFFFF"
adaptive_icon_foreground: "assets/icon/icon_foreground.png"
dart run flutter_launcher_icons
adaptive_icon_*— адаптивная иконка Android: передний план отдельно от фона, чтобы система могла обрезать её кругом или квадратом.- Команда сама сгенерирует все размеры в
android/app/src/main/res/иios/Runner/Assets.xcassets/.
Splash-экран аналогично генерирует пакет flutter_native_splash командой dart run flutter_native_splash:create.
Android: подпись приложения
Каждый APK/AAB должен быть подписан. Подпись — как печать компании: доказывает, что обновление пришло от того же разработчика.
Шаг 1. Создаём ключ загрузки
keytool -genkey -v -keystore ~/upload-keystore.jks \
-keyalg RSA -keysize 2048 -validity 10000 -alias upload
Команда спросит пароль и данные владельца. На выходе — файл upload-keystore.jks. keytool входит в JDK; если команды нет, используйте ту, что лежит внутри Android Studio.
Шаг 2. Файл key.properties
Создайте android/key.properties:
storePassword=YOUR_STORE_PASSWORD
keyPassword=YOUR_KEY_PASSWORD
keyAlias=upload
storeFile=/Users/you/upload-keystore.jks
Шаг 3. Подключаем ключ в Gradle
В android/app/build.gradle.kts:
import java.util.Properties
import java.io.FileInputStream
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
// ...
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}
- Первые строки читают
key.properties, если он есть. signingConfigsописывает ключ, аbuildTypes.releaseговорит «подписывай release этим ключом».
Теперь flutter build appbundle --release (с вашим --flavor и --dart-define-from-file) выдаст подписанный AAB.
Play App Signing
В Google Play используется схема из двух ключей. Вы подписываете файл ключом загрузки (upload key), а Google переподписывает приложение своим ключом приложения, который хранится у него. Если ключ загрузки утечёт или потеряется, его можно заменить через поддержку — приложение не «умрёт».
Google Play: внутреннее тестирование
- Зарегистрируйте аккаунт разработчика в Google Play Console (разовый взнос).
- Create app: название, язык, приложение или игра, бесплатное или платное.
- Заполните раздел App content: политика конфиденциальности, Data safety (какие данные собираете), возрастной рейтинг, целевая аудитория, реклама.
- Testing → Internal testing → Create new release, загрузите
.aab, напишите, что нового. - Добавьте тестировщиков списком email и отправьте им ссылку-приглашение.
| Трек | Для кого | Особенности |
|---|---|---|
| Internal testing | До 100 человек, ваша команда | Доступно почти сразу, без полной проверки |
| Closed testing | Выбранные группы | Нужно для новых личных аккаунтов перед продакшеном |
| Open testing | Любой желающий | Публичная страница в магазине |
| Production | Все пользователи | Полная проверка Google, можно раскатывать постепенно (staged rollout) |
iOS: сборка и TestFlight
Для публикации в App Store нужен Mac с Xcode и платный аккаунт Apple Developer Program.
Шаг 1. Bundle ID и подпись
- Откройте
ios/Runner.xcworkspaceв Xcode. - Runner → Signing & Capabilities: выберите свою команду (Team) и включите Automatically manage signing. Xcode сам создаст сертификаты и provisioning profile.
- Проверьте Bundle Identifier — он должен совпадать с тем, что вы создадите в App Store Connect.
- Здесь же добавляйте capabilities — Push Notifications, Associated Domains (для Universal Links) и т. д.
Сертификат подтверждает, кто вы, а provisioning profile — что это приложение с этим Bundle ID можно подписать вашим сертификатом. Автоматическое управление избавляет от ручной возни.
Шаг 2. Карточка в App Store Connect
В App Store Connect → Apps → New App: платформа iOS, название, язык, Bundle ID, SKU (любой внутренний код). Затем заполните App Privacy — какие данные собирает приложение (включая те, что собирают Firebase Analytics и Crashlytics).
Шаг 3. Сборка и загрузка
flutter build ipa --release --flavor prod --dart-define-from-file=config/prod.json
Готовый файл — в build/ios/ipa/. Загрузить его можно приложением Transporter от Apple (перетащить .ipa) или через Xcode: откройте build/ios/archive/Runner.xcarchive и нажмите Distribute App в окне Organizer.
Шаг 4. TestFlight
TestFlight — официальная бета-площадка Apple. Через 10–30 минут после загрузки сборка появится во вкладке TestFlight.
| Внутренние тестировщики | Внешние тестировщики | |
|---|---|---|
| Кто | Участники вашей команды в App Store Connect | Любые люди по email или публичной ссылке |
| Лимит | До 100 | До 10 000 |
| Проверка Apple | Не нужна | Нужна Beta App Review для первой сборки версии |
Тестировщики устанавливают приложение TestFlight из App Store и получают вашу сборку. Сборка живёт 90 дней.
README: лицо проекта
README.md — первое, что видит новый разработчик (и работодатель, открывший ваш GitHub). Хороший README отвечает на вопросы: что это, как запустить, как устроено.
# Shop — интернет-магазин на Flutter
Мобильное приложение магазина: каталог, корзина, авторизация, чат с поддержкой.
## Стек
Flutter 3, Dart 3, flutter_bloc, dio, get_it, firebase (auth, database, analytics, crashlytics)
## Архитектура
Clean Architecture: lib/features/<feature>/{data, domain, presentation}
## Запуск
1. flutter pub get
2. Скопируйте config/dev.example.json в config/dev.json и заполните значения
3. make run-dev
## Сборка
make build-android — AAB для Google Play
make build-ios — IPA для App Store
## Скриншоты
(3–4 скриншота основных экранов)
В реальном README команды оформляют блоками кода, а скриншоты кладут в папку docs/. Главное правило: человек должен запустить проект за 5 минут, ни о чём вас не спрашивая.
Makefile: команды в одно слово
Команды сборки длинные, их легко забыть или ошибиться в флаге. Makefile — файл с короткими псевдонимами для таких команд. Утилита make есть на macOS и Linux из коробки.
.PHONY: get gen run-dev build-android build-ios clean
get:
flutter pub get
gen:
dart run build_runner build --delete-conflicting-outputs
run-dev:
flutter run --flavor dev --dart-define-from-file=config/dev.json
build-android:
flutter build appbundle --release --flavor prod \
--dart-define-from-file=config/prod.json \
--obfuscate --split-debug-info=build/symbols
build-ios:
flutter build ipa --release --flavor prod \
--dart-define-from-file=config/prod.json \
--obfuscate --split-debug-info=build/symbols
clean:
flutter clean && flutter pub get
Запуск: make gen, make build-android.
get:— имя команды (цель, target), под ней — что выполнить..PHONY— говоритmake, что это команды, а не имена файлов.\в конце строки — перенос длинной команды.
Типичные ошибки
- «Version code has already been used» — не увеличили номер сборки после
+. - Приложение подписано debug-ключом — Google Play не примет такой файл; проверьте
signingConfigвrelease. - Прозрачная иконка на iOS — App Store отклонит загрузку; используйте
remove_alpha_ios: true. - Ключ в репозитории —
key.propertiesи.jksзакоммичены в Git. - Bundle ID в Xcode не совпадает с App Store Connect — загрузка не находит приложение.
Практика
- Сгенерируйте свою иконку через
flutter_launcher_iconsи поднимите версию до1.0.0+1. Ожидаемый результат: на телефоне новая иконка, в настройках приложения — версия 1.0.0. - Создайте ключ загрузки, настройте подпись и соберите
flutter build appbundle. Ожидаемый результат: подписанный AAB вbuild/app/outputs/bundle/, аkey.propertiesне попал вgit status. - Напишите Makefile с целями
get,gen,run-dev,build-android. Ожидаемый результат:make build-androidсобирает релиз одной командой. - Оформите README для своего проекта по шаблону из урока. Ожидаемый результат: одногруппник запускает проект по README без ваших подсказок.
- Если есть доступ к аккаунтам разработчика — загрузите сборку во внутреннее тестирование Google Play или TestFlight и установите её на свой телефон. Ожидаемый результат: приложение ставится из магазина.
Итоги
- Идентификатор приложения выбирают один раз и навсегда;
com.exampleне годится. version: 1.4.2+17— версия для людей и номер сборки, который растёт с каждой загрузкой.- Иконки и splash генерируют пакетами, а не вручную.
- Android-релиз подписывается ключом загрузки; ключ и пароли не попадают в Git.
- Сначала — внутреннее тестирование в Google Play и TestFlight, потом продакшен.
- README объясняет, как запустить проект, а Makefile превращает длинные команды в короткие.