Русский
Русский
English
Статистика
Реклама

Flutter Flavoring

Хотелось ли вам иметь несколько версий одного приложения?

Чтобы одной командой вы могли собрать приложение под определенное окружение?

Сталкивались ли вы с тем, что одновременно нельзя было установить несколько версий одного приложения на одном устройстве?

Всем привет!

Меня зовут Андрей!

И в этой статье я расскажу, как настроить сборку приложения для разных окружений.

Сразу отмечу, что слова версия, окружение и флейвор (flavor) будут взаимозаменяемыми.

Не смотря на то, что материал называется Flutter Flavoring, бОльшая часть работы будет в нативном пространстве (в папках android/ и ios/). Приведённые мной инструкции используются так же и для нативных приложений, а не только для Flutter приложений.

  • Overview

  • Create the App

  • Переменные окружения в .env

  • Android Flavoring

  • iOS Flavoring

  • App Icons

  • Firebase Projects

  • Заключение

GitHub: https://github.com/AndrewPiterov/flutter_starter_app/

Видео версия на YouTube:

Overview

Мы настроим сборку приложения для двух окружений: DEVELOPMENT и PRODUCTION.

У каждой версии будут свои

  • иконки

  • наименования

  • application ID

  • переменные окружения, т.к. адрес к API серверу

  • Firebase проекты

Начнём...

Create the App

Для начала создадим наш новый флаттер проект и мигрируем его сразу на null safety

$ flutter create flutter_starter_app$ cd flutter_starter_app && dart migrate --apply-changes

Откроем проект в любимом IDE.

Переменные окружения в .env

Первым делом настроим переменные окружения для нашего проекта.

Эти переменные я предпочитаю хранить в файле assets/.env. И в зависимости какую версию приложения мы собираем, мы указываем в этом файле соответствующие переменные. Изменять этот файл будем в CI/CD (Continuous integration & continuous delivery) в следующих статьях, а пока укажем значения в этом файле один раз и продолжим.

# assets/.envENVIRONMENT=devAPI_URI=https://api.mydev.com

Добавим в pubspec.yaml пакет flutter_dotenv, который облегчит нам считывание этого .env файла:

dependencies:# ...    flutter_dotenv: ^4.0.0-nullsafety.0

И укажем, что вместе с проектом идут следующие файлы (assets):

assets:    - assets/

Добавляем класс, который будет считывать наши переменные с этого .env файла и предоставлять доступ к этим переменным через свойства:

import 'package:flutter/foundation.dart';import 'package:flutter_dotenv/flutter_dotenv.dart';import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;class AppConfig {  factory AppConfig() {    return _singleton;  }  AppConfig._();  static final AppConfig _singleton = AppConfig._();  static bool get IS_PRODUCTION =>      kReleaseMode || ENVIRONMENT.toLowerCase().startsWith('prod');  static String get ENVIRONMENT => env['ENVIRONMENT'] ?? 'dev';  static String get API_URI => env['API_URI']!;  Future<void> load() async {    await DotEnv.load(fileName: 'assets/.env');    debugPrint('ENVIRONMENT: $ENVIRONMENT');    debugPrint('API ENDPOINT: $API_URI');  }}

Подгрузим наши переменные окружения в самом начале запуска приложения в main.dart:

Future main() async {  WidgetsFlutterBinding.ensureInitialized();  await AppConfig().load();  runApp(MyApp());}

И где-то на скрине в приложении отобразим наши переменные:

Column(  mainAxisAlignment: MainAxisAlignment.center,  children: [    Text(      AppConfig.ENVIRONMENT,    style: TextStyle(fontSize: 50),    ),    Text(      AppConfig.API_URI,        style: TextStyle(fontSize: 30),    ),  ],)

Запускаем приложение:

$ flutter run

Результат:

Изменим значения в .env, перезапустим приложение, и увидим новые значения на экране.

Не забудьте поместить .env в .gitignore

На этом настройка в Flutter пространстве (в папке lib/) закончена, следующие настройки будут в нативном пространстве, т.е. в папках android/ и ios/.

Android Flavoring

Для Android настройка очень простая. Достаточно указать следующие параметры в android/app/gradle

android {    compileSdkVersion 30// ...    flavorDimensions "starter_app"    productFlavors {        dev {            dimension "starter_app"            applicationIdSuffix ".dev"            resValue "string", "app_name", "Starter(Dev)"            versionNameSuffix ".dev"        }        prod {            dimension "starter_app"            resValue "string", "app_name", "Starter"        }    }

Где указали какие флейворы нам нужны, и у каждого флейвора свой applicationId и наименование.

В AndroidManifest.xml укажем ссылку на переменную app_name с наименованием из флейвора:

<application        ...        android:label="@string/app_name"

Запускаем приложение на Android под каждую версию:

$ flutter run --flavor=dev$ flutter run --flavor=prod

Результат: установилось два приложения с разными наименованиями.

iOS Flavoring

В iOS нет такого понятия как Flavor, которое есть в Android.И в iOS используется Схемы (Schema) и их Конфигурации (Configuration).

На картинке ниже изображено, что у каждой Схемы есть свои Конфигурации. И у каждой Конфигурации есть свои параметры, которые мы можем кастомизировать. Например, applicationId, название приложения и иконки приложения под разные версии.

Первым делом нам нужно добавить наши Схемы, и добавить к каждой схеме её конфигурации. Для этого мы откроем XCode, и сверху нажимаем на Runner -> New scheme и добавляем нашу новую dev Схему.

Далее добавим devконфигурации. Для этого выбираем Project -> Runner, где видим раздел наших Конфигураций. Чтобы добавить новые конфигурации, нам нужно продублировать имеющиеся конфигурации и назвать их соответсnвующим образом с суффиксом -dev, например:

Дальше переименуем нашу Runner схему вprod

Далее нужно привязать dev Конфигурации к dev схеме. На текущий момент у dev схемы указаны Debug, Release, Profile конфигурации (те, что без суффикса -dev), т.к. мы создали новую dev схему когда еще не было -dev конфигураций.

Переименуем Debug, Release, Profile, добавив к ним суффикс -prod:

Сейчас у нас две схемы с их отдельными конфигурациями. И мы можем кастомизировать параметры для каждой отдельной схемы. И первым делом, выставим каждой конфигурации свой applicationId:

Кастомизируем наименование приложения для каждой отдельной конфигурации:

И добавим в ios/Runner/Info.plist новое свойство для нашей переменной:

<dict>...<key>CFBundleDisplayName</key><string>$(APP_DISPLAY_NAME)</string>...</dict>

Запускаем приложение на iOS под каждую версию:

$ flutter run --flavor=dev$ flutter run --flavor=prod

Результат: установилось два приложения с разными наименованиями.


App Icons

Мы воспользуемся плагином flutter_launcher_icons, который сгенерирует для нас иконки для каждой платформы и для каждой версии по отдельности.

dev_dependencies:    # ...    flutter_launcher_icons: ^0.8.1

Добавим в корне проекта файлы конфигурации для этого плагина под каждую версию, в которых укажем какие картинки брать для генерации иконок.

# flutter_launcher_icons-dev.yamlflutter_icons:  android: true  ios: true  # image_path: "assets/app_icon/dev.jpg"  image_path_android: "assets/app_icon/android_dev.png"  image_path_ios: "assets/app_icon/ios_dev.png"
# flutter_launcher_icons-prod.yamlflutter_icons:  android: true  ios: true  # image_path: "assets/app_icon/prod.jpg"  image_path_android: "assets/app_icon/android_prod.png"  image_path_ios: "assets/app_icon/ios_prod.png"

Запускаем следующую команду генерации иконок:

flutter pub run flutter_launcher_icons:main -f flutter_launcher_icons*

И посмотрим, где добавились сгенерированные иконки:

Для Android все готово, но для iOS нужно снова вернуться в XCode и так же, как и в случае с наименованием и application ID, указать у каждой конфигурации свою иконку:

Запускаем приложение под каждую версию на iOS и Android, и увидим результат - иконки наших уже установленных приложений обновились:


Firebase Projects

Прежде всего создадим два Firebase проекта под каждую версию через firebase console .

В каждом проекте добавим Android и iOS приложения и скачаем файлы конфигурации Firebase проектов:

  • google-services.json Android приложения - 2 штуки

  • GoogleService-Info.plist iOS приложения - 2 штуки

Для теста, можем для каждого Firebase проекта активировать Firestore, в котором одна коллекция secrets с одним элементом, у которого есть поле value. У prod версии значение в value равно PRODUCTION, у dev версии - DEVELOPMENT.

В pubscpec.yaml добавляем Firebase зависимости

dependencies:# ...    # Firebase    firebase_core: ^1.1.0    cloud_firestore: ^2.0.0

В main.dart проинициализируем Firebase приложение

Future main() async {// ...await Firebase.initializeApp();    runApp(MyApp());}

И для теста, где-то на скрине приложения отобразим наше значение value

StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(  stream:      FirebaseFirestore.instance        .collection('secrets').snapshots(),  builder: (_, snapshot) {    if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {      return CircularProgressIndicator();    }    final first = snapshot.data!.docs.first.data();    return Text(      'Firebase: ' + first['value'],      style: TextStyle(        fontSize: 25,        fontWeight: FontWeight.bold,        color: Colors.blue,      ),    );  },),

Настроим iOS и Android для Firebase. Более подробно о настройке можно почитать на официальном сайте.

Настройка Firebase на iOS

В файле ios/Podfile укажем минимальную версию iOS 10

platform :ios, '10'

И в этом же фале в методе target 'Runner' добавим следующую строчку, из-за которой наше приложение будет собираться быстрее:

# ...target 'Runner' do  pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '7.11.0'# ...end

Далее кладем файлы конфигурации для Firebase в проекте в папках config/prod и config/dev

И добавим новый Build Phase Script, указанный ниже, который будет во время сборки определенной версии приложения брать соответсвующий файл Firebase конфигурации и помещать его в папку Runner:

environment="default"# Regex to extract the scheme name from the Build Configuration# We have named our Build Configurations as Debug-dev, Debug-prod etc.# Here, dev and prod are the scheme names. This kind of naming is required by Flutter for flavors to work.# We are using the $CONFIGURATION variable available in the XCode build environment to extract # the environment (or flavor)# For eg.# If CONFIGURATION="Debug-prod", then environment will get set to "prod".if [[ $CONFIGURATION =~ -([^-]*)$ ]]; thenenvironment=${BASH_REMATCH[1]}fiecho $environment# Name and path of the resource we're copyingGOOGLESERVICE_INFO_PLIST=GoogleService-Info.plistGOOGLESERVICE_INFO_FILE=${PROJECT_DIR}/config/${environment}/${GOOGLESERVICE_INFO_PLIST}# Make sure GoogleService-Info.plist existsecho "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_FILE}"if [ ! -f $GOOGLESERVICE_INFO_FILE ]thenecho "No GoogleService-Info.plist found. Please ensure it's in the proper directory."exit 1fi# Get a reference to the destination location for the GoogleService-Info.plist# This is the default location where Firebase init code expects to find GoogleServices-Info.plist filePLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.appecho "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"# Copy over the prod GoogleService-Info.plist for Release buildscp "${GOOGLESERVICE_INFO_FILE}" "${PLIST_DESTINATION}"

Называем эту Build Phase понятным именем и перемещаем ее немного выше:

Не забудьте поместить GoogleService-Info.plist в .gitignore

Запускаем приложение и видим результат.

Настройка Firebase на Android

Первое добавим зависимость для плагина google services в android/build.gradle

# android/build.gradlebuildscript {  dependencies {    // ... other dependencies    classpath 'com.google.gms:google-services:4.3.3'  }}

Используем плагин в android/app/build.gradle

apply plugin: 'com.google.gms.google-services'

Выставим минимальную версию SDK как 21

android {    defaultConfig {        // ...        minSdkVersion 21            // <------ THIS        targetSdkVersion 28        multiDexEnabled true    }}

Добавим файлы конфигурации Firebase в соответствующие папки каждого флейвора:

Не забудьте поместить google-services.json в .gitignore

Запускаем каждую версию на Андроиде и проверяем результат:


Заключение

Таким образом, мы настроили флейворы или сборку разных версий нашего приложения, что у каждой версии свои:

  • application id

  • иконки

  • наименования

  • переменные окружения

  • Firebase бэкенд

Надеюсь материал был полезен для вас.

Всем happy coding!

Источник: habr.com
К списку статей
Опубликовано: 10.05.2021 16:07:24
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка под ios

Разработка под android

Flutter

Ios

Android

Flavors

Versioning

Firebase

Configuration

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru