Ответ
Да, я настраивал flavors для создания разных сборок приложения (dev, staging, prod) с уникальными параметрами.
Цель: Разделить конфигурации для разных сред: разные API-эндпоинты, ключи Firebase, идентификаторы приложения (bundle ID), иконки и названия.
Настройка для Android (android/app/build.gradle):
android {
...
flavorDimensions "environment"
productFlavors {
dev {
dimension "environment"
// Добавляем суффикс к ID для установки рядом с prod версией
applicationIdSuffix ".dev"
// Задаем другое имя для иконки
resValue "string", "app_name", "MyApp Dev"
// Можно менять переменные для манифеста
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher_dev"]
}
prod {
dimension "environment"
resValue "string", "app_name", "MyApp"
}
}
}
Настройка для iOS (через Xcode):
- Создаем новые Schemes (MyAppDev, MyAppProd).
- Создаем Configurations (Debug-Dev, Release-Dev и т.д.) на основе существующих.
- В
Info.plistдля разных конфигураций задаем разные значения для ключей (например,CFBundleName).
Использование в Dart-коде:
Создаем отдельные entry-point файлы (например, main_dev.dart, main_prod.dart), которые инициализируют приложение с нужными параметрами.
// lib/main_dev.dart
void main() {
// Устанавливаем конфиг для dev среды
final config = AppConfig(
apiBaseUrl: 'https://dev.api.example.com',
appName: 'MyApp Dev',
flavor: 'dev',
);
runApp(MyApp(config));
}
// lib/main_prod.dart
void main() {
final config = AppConfig(
apiBaseUrl: 'https://api.example.com',
appName: 'MyApp',
flavor: 'prod',
);
runApp(MyApp(config));
}
Запуск сборки:
# Запуск dev-версии
flutter run --flavor dev -t lib/main_dev.dart
# Сборка APK для dev
flutter build apk --flavor dev -t lib/main_dev.dart
# Сборка App Bundle для prod
flutter build appbundle --flavor prod -t lib/main_prod.dart
Такой подход полностью исключает ручное изменение конфигов при переключении между средами разработки, тестирования и продакшена.
Ответ 18+ 🔞
А, ну это про flavors, да? Ёпта, я тоже с этим в своё время намучился, пока не разобрался. Слушай, история такая: ты делаешь приложение, а оно должно в дев-версии на тестовый сервер ходить, в стейджинге — на промежуточный, а в проде — уже на боевой, да? И чтобы они все три на одном телефоне могли стоять, как отдельные приложения. Вот это и есть flavors, по сути — разные «вкусы» одной и той же программы.
Зачем это вообще надо, спросишь? Ну, представь: ты тестишь фичу, а она на проде API ключи все сожрёт или хуже того — реальным пользователям какую-нибудь дичь отправит. Это же пиздец! Поэтому и разделяем: дев — для разработки, стейджинг — для тестов перед выпуском, прод — для всех остальных. И чтобы не путаться, у каждой версии своя иконка и название.
Настраиваем на Android (android/app/build.gradle):
Тут всё, в принципе, логично, но с первого раза может не взлететь, будь готов.
android {
...
// Сначала объявляем "измерение" — типа категорию для наших вкусов
flavorDimensions "environment"
productFlavors {
dev {
dimension "environment"
// Добавляем суффикс к ID. Будет не com.yourapp, а com.yourapp.dev
// И их можно будет поставить рядом!
applicationIdSuffix ".dev"
// Меняем имя в лаунчере, чтобы не путать с продом
resValue "string", "app_name", "MyApp Dev"
// Тут можно, например, иконку другую подсунуть
manifestPlaceholders = [appIcon: "@mipmap/ic_launcher_dev"]
}
prod {
dimension "environment"
// А тут оставляем как есть — красивое имя для магазина
resValue "string", "app_name", "MyApp"
}
}
}
Главное, после таких правд не забудь синхронизировать проект в Андроид Студио (эта кнопка «Sync Now» сверху). А то будешь сидеть и думать: «Почему не работает, ёпта?».
Настройка для iOS — это отдельная песня, там ёперный театр. Честно, проще через Xcode делать, потому что Flutter тут не всё за тебя сделает.
- Создаешь новые Schemes (типа MyAppDev, MyAppProd) — это как профили сборки.
- Потом лезешь в настройки проекта и создаешь Configurations (Debug-Dev, Release-Dev и т.д.) на основе старых. Это, блядь, самое нудное.
- И уже в
Info.plistдля каждой конфигурации прописываешь разные значения. Например,CFBundleNameдля дев-версии сделаешь «MyApp Dev».
А как в самом Dart-коде этим пользоваться?
Тут подход такой: делаем отдельные точки входа. То есть не один main.dart, а несколько.
// lib/main_dev.dart
void main() {
// Инициализируем конфиг для dev среды
final config = AppConfig(
apiBaseUrl: 'https://dev.api.example.com', // Тут твой тестовый сервер
appName: 'MyApp Dev',
flavor: 'dev',
);
runApp(MyApp(config)); // И запускаем приложение с этим конфигом
}
// lib/main_prod.dart
void main() {
final config = AppConfig(
apiBaseUrl: 'https://api.example.com', // А тут уже боевой
appName: 'MyApp',
flavor: 'prod',
);
runApp(MyApp(config));
}
Класс AppConfig ты, ясное дело, сам создашь, где все эти поля будут. И в самом приложении уже смотришь на config.flavor и решаешь, куда тебе стучаться.
Сборка и запуск — тут уже просто:
# Запустить дев-версию на подключенном устройстве
flutter run --flavor dev -t lib/main_dev.dart
# Собрать APK для дева (чтобы тестерам раздать)
flutter build apk --flavor dev -t lib/main_dev.dart
# А это уже серьёзно — собрать App Bundle для заливки в прод
flutter build appbundle --flavor prod -t lib/main_prod.dart
Вот и вся магия. Сначала кажется, что овердохуища работы, но зато потом, когда не надо вручную комментировать строки с апишками перед каждым билдом, понимаешь — оно того стоило. Чистая работа, никакого говнокода.