Этот пост будет полезен для 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 набирает популярность и становится все более востребованным, обмен опытом должен оказаться полезен для всех нас.