Есть ли опыт работы с конфигурациями сборки (Flavors)?

Ответ

Да, я настраивал 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):

  1. Создаем новые Schemes (MyAppDev, MyAppProd).
  2. Создаем Configurations (Debug-Dev, Release-Dev и т.д.) на основе существующих.
  3. В 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 тут не всё за тебя сделает.

  1. Создаешь новые Schemes (типа MyAppDev, MyAppProd) — это как профили сборки.
  2. Потом лезешь в настройки проекта и создаешь Configurations (Debug-Dev, Release-Dev и т.д.) на основе старых. Это, блядь, самое нудное.
  3. И уже в 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

Вот и вся магия. Сначала кажется, что овердохуища работы, но зато потом, когда не надо вручную комментировать строки с апишками перед каждым билдом, понимаешь — оно того стоило. Чистая работа, никакого говнокода.