Перейти к содержимому

Инструкции по настройке · Flutter

Закрытое тестирование Google Play для приложений на Flutter

От flutter build appbundle до Подать заявку на доступ к продакшену: как собрать и подписать релизный бандл, настроить закрытый трек, удержать 12 тестировщиков 14 дней и не допустить ошибок Flutter, которые стоят недели.

Сборка
flutter build appbundle
Код версии
pubspec.yaml → version: 1.0.0+1
Целевой API
targetSdk ≥ 36

Соберите релизный app bundle

Для новых приложений Google Play принимает только Android App Bundle (.aab). Flutter собирает его одной командой, а режим release используется по умолчанию. Сначала проверьте две вещи: обе трудно исправить, когда приложение уже у тестировщиков.

  1. Задайте ID приложения

    В android/app/build.gradle.kts укажите для applicationId уникальное значение в формате обратного домена, например com.yourcompany.app. После первой загрузки в Google Play его нельзя изменить.

  2. Проверьте разрешение INTERNET

    Если приложение работает с сетью, в android/app/src/main/AndroidManifest.xml должно быть объявлено android.permission.INTERNET. В манифестах debug и profile оно есть, а в release появится, только если вы его добавите.

  3. Соберите бандл

    Выполните команду ниже в корне проекта. Flutter сохранит бандл в build/app/outputs/bundle/release/.

Terminal
flutter build appbundle

Используете flavors? Соберите тот, который публикуете, например flutter build appbundle --flavor production, и проверьте, что он использует продакшен-имя пакета и продакшен-ключи API.

Подпись: ключ загрузки и подписание приложений в Google Play

Для новых приложений подписание в Google Play обязательно: ключ подписи приложения хранит Google, а каждую загрузку вы подписываете своим ключом загрузки. Если ключ загрузки потеряется, его можно сбросить через Google; само приложение при этом не теряется. Создайте ключ с помощью keytool:

Terminal (macOS / Linux)
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
        -storetype JKS -keysize 2048 -validity 10000 -alias upload

Затем создайте android/key.properties с паролями и путём к хранилищу ключей. Ни этот файл, ни .jks не должны попадать в систему контроля версий:

android/key.properties
storePassword=<keystore password>
keyPassword=<key password>
keyAlias=upload
storeFile=/Users/<you>/upload-keystore.jks

Загрузите файл в android/app/build.gradle.kts и привяжите тип сборки release к новой конфигурации подписи:

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.getProperty("keyAlias")
            keyPassword = keystoreProperties.getProperty("keyPassword")
            storeFile = keystoreProperties.getProperty("storeFile")?.let { file(it) }
            storePassword = keystoreProperties.getProperty("storePassword")
        }
    }
    buildTypes {
        release {
            signingConfig = signingConfigs.getByName("release")
        }
    }
}

В старых проектах вместо build.gradle.kts используется build.gradle (Groovy); поля те же. После изменения файлов Gradle выполните flutter clean.

Код версии и целевой уровень API

У каждой загрузки код версии должен быть больше, чем у любого ранее загруженного бандла в любом треке. Во Flutter это число после + в pubspec.yaml:

pubspec.yaml
version: 1.0.0+1

1.0.0 становится названием версии, которое видят пользователи, а 1 — кодом версии. Увеличивайте его до +2, +3 и так далее или переопределяйте оба значения при сборке через --build-name и --build-number.

С 31 августа 2026 года новые приложения и обновления должны быть нацелены на Android 16 (уровень API 36) или выше. В файле Gradle стоит targetSdk = flutter.targetSdkVersion, поэтому обычно его повышают обновлением Flutter. Если ваша версия Flutter старее, укажите targetSdk = 36 вручную и перед загрузкой протестируйте приложение на свежей версии Android.

Ловушки Flutter, из-за которых затягивается закрытый тест

  • Повторный номер сборки. Play Console отклоняет бандл, если его код версии уже использовался. Увеличивайте +N в pubspec.yaml перед каждой сборкой, которую собираетесь загрузить.
  • Обфускация без символов. --obfuscate работает только вместе с --split-debug-info. Сохраняйте папку с символами каждой загруженной сборки, иначе вы не сможете прочитать отчёты о сбоях от тестировщиков.
  • Не тот flavor. С flavors легко загрузить staging-сборку со staging-адресами. Тогда тестировщики сообщают об ошибках, которых продакшен-пользователи никогда не увидят.
  • Нет сети в release. Если в основном манифесте нет разрешения INTERNET, приложение работает в debug и молча ломается у тестировщиков.
  • Бандл с отладочной подписью. Если тип сборки release всё ещё использует отладочную подпись, Play Console отклонит загрузку.
  • Плагины со старым нативным кодом. Плагины с библиотеками .so должны поддерживать страницы памяти 16 КБ. Play Console предупреждает об этом, а с 1 февраля 2027 года обновления без такой поддержки нельзя будет выпустить. Обновите плагины и Flutter SDK до теста, а не посреди него.
Terminal
flutter build appbundle --obfuscate --split-debug-info=build/symbols

Читайте отчёты о сбоях от тестировщиков

Тестировщики вставляют трассировки стека в отчёты об ошибках, а Play Console собирает сбои в разделе Мониторинг и улучшение → Android Vitals → Сбои и ANR. У трассировки Flutter две половины: часть Dart, которую --obfuscate превращает в короткие бессмысленные имена, и часть Android, которую сжимает R8. Flutter всегда включает сжатие кода в релизных сборках, поэтому готовьтесь к обеим.

  1. Сохраняйте символы Dart

    Сборка с --split-debug-info записывает по файлу символов на архитектуру, например app.android-arm64.symbols. Храните папку вместе с кодом версии, к которому она относится: файл от другой сборки не расшифрует трассировку.

  2. Расшифровывайте трассировки Dart

    Сохраните трассировку в файл и передайте её flutter symbolize вместе с файлом символов для архитектуры устройства. Большинство современных телефонов работают на arm64.

  3. Java-часть оставьте Play Console

    Начиная с Android Gradle plugin 4.1, файл сопоставления R8 входит в .aab, поэтому Play Console деобфусцирует строки Java и Kotlin из плагинов без отдельной загрузки.

Terminal
flutter symbolize -i crash.txt -d build/symbols/app.android-arm64.symbols

Обфускация только переименовывает символы: она ничего не шифрует и не подходит для хранения секретов. Код, который сравнивает runtimeType.toString() с именем класса, ломается после обфускации, а имена enum остаются читаемыми.

Коротко о Play Console: от закрытого теста до продакшена

Когда бандл Flutter собран и подписан, всё остальное происходит в Play Console и одинаково для любого фреймворка. Здесь краткий чек-лист; каждое меню и экран описаны в полном руководстве по Play Console.

  • Завершите настройку приложения на панели управления: политика конфиденциальности, доступ к приложению, реклама, возрастной рейтинг, целевая аудитория, безопасность данных и страница в Play Маркете.
  • Создайте выпуск для закрытого теста в разделе Тестирование и выпуск → Тестирование → Закрытое тестирование: выберите страны тестировщиков, загрузите .aab и отправьте на проверку.
  • Добавьте тестировщиков через Google Группу на вкладке Тестировщики и поделитесь ссылкой opt-in. Она заработает, когда у трека появится статус Опубликовано.
  • Держите не менее 12 тестировщиков с opt-in 14 дней подряд. Внутреннее тестирование не засчитывается, а у того, кто вышел и вернулся, отсчёт 14 дней начинается заново.
  • Выпустите хотя бы одно обновление во время теста и запишите, что нашли тестировщики и что вы изменили.
  • Подайте заявку на доступ к продакшену с панели управления и ответьте на три раздела о тесте, приложении и готовности. Решение принимает Google; никто не может гарантировать одобрение.

Учитывайте выбывших: наберите больше 12 тестировщиков, чтобы уход одного человека не сдвинул ваши сроки. Все шаги в Play Console.

Полное руководство по Play Console

Закрытый трек, Google Группа и ссылка opt-in, правило 12 тестировщиков / 14 дней и заявка на продакшен, шаг за шагом на одной странице.

Открыть шаги в Play Console

Выпускайте исправления во время теста

Во Flutter: увеличьте номер сборки, пересоберите и загрузите новый бандл в тот же закрытый трек.

Terminal
flutter build appbundle --build-name=1.0.1 --build-number=2

Перед каждой загрузкой запускайте релизную сборку на реальном телефоне. flutter install устанавливает релизный APK на подключённое устройство и за минуты выявляет недостающие разрешения, не тот flavor и сбои, которые бывают только в релизе:

Terminal
flutter build apk
flutter install

Чтобы проверить именно тот бандл, который вы собираетесь загрузить, превратите его в APK для устройства с помощью bundletool от Google или загрузите в закрытый трек и установите из Google Play с аккаунта тестировщика. Так вы тестируете то, что получают тестировщики, включая разделённые по устройствам APK от Play.

  • Одно конкретное изменение на обновление. По небольшим выпускам видно, какое исправление ответило на какой отчёт, а в форме доступа к продакшену у вас будут конкретные примеры.
  • Не меняйте версию Flutter посреди теста, если этого не требует ошибка от тестировщиков. Обновление SDK одновременно меняет движок, нативный код каждого плагина и, возможно, targetSdk.
  • Оставьте время на проверку. Каждый новый выпуск в закрытом треке уходит в Google, прежде чем попасть к тестировщикам, поэтому не откладывайте последнее исправление на последний день.

Частые вопросы

Нужен ли --obfuscate для закрытого теста?

Нет. Обфускация необязательна и лишь затрудняет чтение символов Dart. Если вы её используете, всегда добавляйте --split-debug-info и архивируйте символы, иначе сбои от тестировщиков будет невозможно прочитать.

Какой код версии видит Google Play?

Число после + в pubspec.yaml или значение --build-number, если вы его передаёте. Flutter использует имя сборки как versionName, а номер сборки как versionCode.

Можно ли дать тестировщикам APK?

Не через Google Play: новые приложения загружаются только в формате .aab, и для 12 тестировщиков засчитываются только установки из закрытого трека. Но flutter build apk --split-per-abi удобен для быстрых проверок на ваших собственных телефонах.

Почему Play Console предупреждает о страницах памяти 16 КБ?

Обычно плагин поставляет нативные библиотеки .so, не выровненные под страницы 16 КБ. Обновите плагин и Flutter, пересоберите и проверьте результат на эмуляторе Android с системным образом со страницами 16 КБ до следующей загрузки.

Источники

Технические детали сверены с официальной документацией ниже. Меню и значения по умолчанию меняются от версии к версии; если сомневаетесь, ориентируйтесь на официальную страницу.

Впервые слышите о правиле 12 тестировщиков?

Общее руководство объясняет требование Google, как день за днём проходит кампания TestBunny и что мы можем и не можем гарантировать.

Читать общее руководство

Нужны 12 настоящих тестировщиков на 14 дней?

Добавьте приложение и ссылку на закрытое тестирование, выберите Starter (15 тестировщиков) или Pro (25 тестировщиков) и следите за активностью каждого тестировщика по дням в своей панели.

TestBunny не связан с Google. Тестировщики получают кредиты, а не деньги. Никто не может гарантировать решение Google о доступе к продакшену, и мы тоже.