Этот пост будет полезен для Flutter-разработчиков, которым хочется поработать с флейворами (flavours), изменить и дополнить их. Сегодня мы не будем говорить о процессе создания флейворов с разными иконками, названиями и идентификаторами, благо таких публикаций существует уже очень много. Вместо этого речь пойдет о том, как определять флейвор в Dart-коде, а также о решении некоторых часто встречающихся при сборке приложений проблем, связанных с введением флейворов.
Почему можем об этом рассказать: наше мобильное приложение для модуля HCM-платформы TalentTech Обучение создано на Flutter.
Итак, я рассчитываю, что вы знаете, что такое флейвор и зачем он нужен (если нет, почитайте, например, вот этот материал). Поэтому мы сразу перейдем к тем вопросам, с которыми сталкивается разработчик Flutter, когда у него возникает задача определить новый флейвор.
Определение нового флейвора
Выбрать стандартный флейвор достаточно просто. Для этого необходимо указать его имя в настройках фреймворка. Например, в Android Studio нужно перейти в Run → Edit Configurations → и указать имя флейвора в поле Build Flavor вот так:

Но если требуется что-то более оригинальное, необходимо подготовить ваше приложение для работы с другим флейфором. Для этого можно добавить несколько строк кода на Dart:
import 'dart:async'; import 'package:flutter/services.dart'; enum MyFlavor { staging, prod } Future<MyFlavor> getCurrentFlavor() async { try { final flavorString = await const MethodChannel('flavor').invokeMethod<String>('getFlavor'); return flavorString == "staging" ? MyFlavor.staging : MyFlavor.prod; } catch (e) { return MyFlavor.prod; } }
Android
В окружении Android для определения флейвора нужно модифицировать файл android/app/src/main/kotlin/<app_package>/MainActivity.kt, а также определить обработчик для MethodChannel в методе configureFlutterEngine(). Сделать это можно следующим образом:
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugins.GeneratedPluginRegistrant import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { companion object { const val CHANNEL = "flavor" } override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); // Method channel MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .setMethodCallHandler { call, result -> if (call.method == "getFlavor") { result.success(BuildConfig.FLAVOR); } else { result.notImplemented() } } } }
iOS
Под iOS процесс будет более многоступенчатым и потребует выполнить ряд ручных действий:
1.Для начала, точно также как и для Android, нужно определить обработчик для MethodChannel в методе application:didFinishLaunchingWithOptions. Сделать это необходимо в ios/Runner/AppDelegate.swift. Для этого достаточно выполнить следующий код:
override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) // Method channel if let controller = self.window.rootViewController as? FlutterBinaryMessenger { let channel = FlutterMethodChannel(name: "flavor", binaryMessenger: controller) channel.setMethodCallHandler { (call, result) in let flavor = Bundle.main.infoDictionary?["Flavor"] result(flavor) } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) }
2.Открыть ios/Runner/Info.plist. Добавить ключ Flavor типа String со значением ${PRODUCT_FLAVOR}:

3.Открыть Targets → Runner → Build Settings, нажать на “+”, выбрать Add User-Defined Setting и добавить настройку с названием PRODUCT_FLAVOR. Для каждой конфигурации задать имя флейвора, которое будет передаваться во Flutter.

4.Если для какого-то флейвора изменялся application identifier, нужно создать provisioning profile для него:
Пойти на developer.apple.com → Certificates, Identifiers & Profiles → Identifiers → + → App IDs → …
Выбрать Bundle ID explicit и указать там новый application identifier
В capabilities выбрать те же самые параметры, что и у основного identifier
Выполнить fastlane match --app_identifier “<YOUR_NEW_IDENTIFIER>“
Проблемы при сборке
Нередко при сборке приложения с обновленный флейвором возникают ошибки. На нашем опыте они чаще всего встречаются под iOS. И сегодня разберем две самые популярные из них:
ERROR: iOS архив успешно собран, но выдается неизвестная ошибка
Подобная ошибка может быть вызвана невалидным названием флейвора. В нашем случае не принимались квадратные скобки в названии (а конкретно в строке “[S] TT обучение”). Поэтому, если у вас возникает подобная ошибка, попробуйте сменить название, убрав из него нестандартные символы.
ERROR: Ошибка сертификата на iOS
В реальности описание такой ошибки может выглядеть следующим образом:
Для подобной ситуации было найдено проверенное решение. Разобраться с невалидными провижнами и сертификатами можно в несколько шагов:
Зайти в Runner → Target → Signing & Capabilities и проверить, что нигде нет красных восклицательных знаков
Зайти в Runner → Target → Build Settings, выполнить поиск по слову provision и попытаться найти, есть ли какая-то ошибка в результатах
Если запускаете на локальной машине:
Скачать провижны с помощью fastlane match
Зайти в Xcode → Preferences → Accounts → выбрать нужный аккаунт → Download Manual Profiles.
Перейти к п1.
Если есть ошибки, то убить процесс Xcode, запустить заново и проверить еще раз (это реально помогает).
Если все это не сработало, поискать дополнительные подсказки можно в документации здесь или здесь, а также на https://stackoverflow.com
Заключение
Флейворы — полезная и удобная штука, но при попытке работать с ними могут возникать ошибки и сложности. Надеюсь, что этот небольшой пост поможет вам избежать проблем с определением флейвора, а также окажется полезен тем, у кого возникли те же ошибки при сборке приложения. Учитывая, что Flutter набирает популярность и становится все более востребованным, обмен опытом должен оказаться полезен для всех нас.
