Когда приложение выходит за пределы пет-проекта, возникает необходимость держать окружения раздельно.
Dev,
Staging и
Production должны жить своей жизнью: с разными API-ключами, бэкендом, а иногда даже иконками и названиями. В
Flutter эта задача решается через
Flavors. Рассказываю, как настроить и не запутаться.
Что такое Flavors и зачем они нужны:
Flavors (или схемы в iOS, продуктные варианты в Android) позволяют из одной кодовой базы собирать разные варианты приложения. У каждого варианта могут быть свои:
- URL бэкенда и ключи API.
- Имя приложения и bundle ID.
- Иконки и сплеш-скрины.
- Настройки сборки и зависимости.
Это дает возможность установить на устройство одновременно dev-версию и продовую, не боясь, что они перезатрут друг друга. И главное - исключает случайную отправку тестового кода в релиз.
Организация кода:
Самый простой способ - сделать отдельные точки входа для каждого окружения. В папке
lib создаем файлы:
- main_dev.dart
- main_staging.dart
- main_prod.dart
В каждом передаем в приложение идентификатор среды, чтобы внутри можно было подставлять нужные конфиги.
void main() {
runApp(MyApp(environment: 'DEV'));
}
Android - настройка productFlavors:
В
android/app/build.gradle добавляем секцию:
flavorDimensions "default"
productFlavors {
dev {
dimension "default"
applicationIdSuffix ".dev"
versionNameSuffix "-dev"
}
staging {
dimension "default"
applicationIdSuffix ".staging"
versionNameSuffix "-staging"
}
production {
dimension "default"
}
}
Это даст разные имена пакетов:
.dev,
.staging и основное. Приложения не будут конфликтовать при установке.
iOS - схемы и бандлы:
В
Xcode нужно продублировать схему
Runner для каждого окружения и задать разные идентификаторы бандла в настройках таргета. Например:
- com.example.app.dev
- com.example.app.staging
- com.example.app
В
Info.plist можно выставить разные названия приложений, чтобы в меню было видно, какая версия запущена.
Запуск и сборка:
Для запуска нужного
flavor используем флаги:
flutter run --flavor dev -t lib/main_dev.dart
flutter build apk --flavor prod -t lib/main_prod.dart
flutter build ios --flavor staging -t lib/main_staging.dart
Управление конфигурацией:
Внутри кода удобно сделать класс с константами для каждого окружения:
class AppConfig {
static const Map apiUrls = {
'DEV': 'https://dev.api.example.com',
'STAGING': 'https://staging.api.example.com',
'PROD': 'https://api.example.com',
};
}
А в приложении просто обращаться по ключу, который пришел из main-файла.
Иконки для каждого flavor:
Пакет
flutter_launcher_icons умеет генерировать иконки под разные
flavors. В
pubspec.yaml прописываем:
flutter_launcher_icons:
flavors:
dev:
image_path: "assets/icons/dev_icon.png"
staging:
image_path: "assets/icons/staging_icon.png"
production:
image_path: "assets/icons/prod_icon.png"
Запускаем и получаем разные иконки для каждого окружения.
Лучшие практики:
- Не храните ключи и секреты прямо в коде. Используйте .env файлы или безопасное хранилище.
- Ведите имена flavors одинаково на Android и iOS - меньше путаницы.
- Настройте CI/CD так, чтобы пайплайны собирали каждый flavor отдельно.
- Для тестировщиков dev-сборка должна визуально отличаться (иконка, цвет темы), чтобы случайно не перепутали с продакшеном.
Вывод:
Flavors - это не про «сделать красиво», а про контроль и безопасность. Правильная настройка окружений убережет от случайных деплоев с тестовыми данными и позволит команде спокойно работать, не боясь что-то сломать в бою. Один раз настроив, вы сэкономите часы нервотрепки и багов на пустом месте.