Конфигурационные файлы
Нативные файлы Android и iOS правятся прямо во FlutterFlow, не покидая редактора.
Нужно это обычно при подключении сторонних пакетов — аналитики, рекламных сетей, платёжных сервисов.
Какие файлы доступны:
AndroidManifest.xml— разрешения, метаданные и intent-фильтры Android.build.gradle— сборка Android: версии SDK, зависимости, типы сборки, подпись.- ProGuard — сжатие и обфускация кода в релизной сборке Android.
Info.plist— настройки приложения iOS, включая разрешения.Entitlements.plist— привилегии iOS: push-уведомления, Apple Pay и прочее.AppDelegate.swift— запуск приложения iOS: регистрация плагинов Flutter, инициализация сервисов вроде Firebase, события жизненного цикла и диплинки.main.dart— точка входа приложения Flutter.
Ошибка в нативном файле обходится дорого: пропущенный тег или неверный ключ ломают сборку или роняют приложение при запуске, а лишние разрешения приводят к отказу при публикации в магазине.
Правьте нативный код только когда это действительно нужно, записывайте, что именно поменяли, и проверяйте приложение после каждой правки.
Как править
Есть два способа: вставка фрагментов и ручной режим.
Способ 1: фрагменты
Фрагмент — небольшой кусок кода, который FlutterFlow сам вставляет в нужное место файла. Открывать весь файл не приходится, и это безопаснее: так добавляют разрешение или тег с метаданными.
Фрагменты для Android
В AndroidManifest.xml фрагмент вставляется в один из трёх участков:
- Activity Tags — внутрь блока
MainActivity. Сюда идут<intent-filter>и<meta-data>: диплинки, тема, режим запуска. - Application Tags — атрибуты самого тега
<application>:android:icon,android:label,android:allowBackup. - App Component Tags — целые компоненты внутри
<application>...</application>: дополнительные activity, службы, приёмники широковещательных сообщений, провайдеры содержимого.
Откройте Custom Code в меню слева, выберите Configuration Files и файл AndroidManifest.xml. Нажмите плюс (+) у нужного участка, задайте имя фрагмента — оно попадёт в файл комментарием — и вставьте код.
Фрагменты для iOS
У iOS нет вложенной структуры application/activity: Info.plist и Entitlements.plist — это словари пар «ключ-значение». Фрагмент попадает прямо в корневой элемент <dict>.
Порядок тот же: Custom Code > Configuration Files, нужный файл, кнопка плюс (+), имя фрагмента и код.
- Для
main.dartфрагменты недоступны — только ручной режим. - Во фрагментах можно использовать значения окружения и значения библиотеки — см. переменные в нативном коде.
Способ 2: ручной режим
Manual Edit Mode открывает файл целиком — как обычный текстовый редактор внутри FlutterFlow.
Откройте Custom Code > Configuration Files, выберите файл и нажмите значок замка.
После разблокировки файл остаётся в ручном режиме, пока вы не заблокируете его обратно. Повторная блокировка возвращает файл к версии, которую генерирует FlutterFlow, — все ручные правки при этом теряются.
- Не удаляйте то, что добавил FlutterFlow, если не уверены. Безопаснее только дописывать.
- Ручной режим оправдан для крупных или сложных правок: переставить теги, что-то убрать, вставить большой блок настроек. После этого обязательно проверьте, что приложение собирается и запускается.
- Здесь тоже доступны значения окружения и значения библиотеки.
Переменные в нативном коде
Ключи API и настройки, зависящие от окружения, не стоит прописывать в AndroidManifest.xml, Info.plist и прочих файлах прямо значением. Вместо этого берут значения окружения и значения библиотеки.
Схема такая: создаётся переменная уровня файла, ей назначается значение окружения или библиотеки, а в самом файле ставится подстановка вида {{apiToken}}. При генерации кода подстановки заменяются настоящими значениями.
- Подстановку
{{variableName}}можно вписать сразу в код — переменную уровня файла FlutterFlow создаст сам. - Одна и та же переменная уровня файла доступна всем фрагментам этого файла.
Несколько примеров.
Пример 1: ключ API в AndroidManifest.xml
Пакету Mapbox нужен токен в манифесте. Вместо самого токена ставится подстановка:
<meta-data
android:name="com.mapbox.token"
android:value="{{MAPBOX_ACCESS_TOKEN}}"/>
Здесь {{MAPBOX_ACCESS_TOKEN}} — переменная уровня файла со значением окружения.
Пример 2: настройка Info.plist
Разрешение соединений без HTTPS через App Transport Security:
<key>NSAllowsArbitraryLoads</key>
<{{ALLOW_HTTP_TRAFFIC}}/>
Если ALLOW_HTTP_TRAFFIC в значениях окружения равно true, приложение разрешит запросы по HTTP.
Пример 3: значения библиотеки
Если вы собираете библиотеку FlutterFlow, ключи в нативном коде задаются через значения библиотеки — тогда каждый, кто её подключит, подставит свои.
<application>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="{{WEATHER_API_KEY}}" />
</application>
Пользователь библиотеки задаёт своё значение при подключении, а при сборке {{WEATHER_API_KEY}} заменяется на него.
Что можно править
AndroidManifest.xml (Android)
AndroidManifest.xml — главный файл настроек приложения Android. Он лежит в android/app/src/main и сообщает системе и Google Play всё существенное: имя пакета, компоненты (activity, службы, приёмники) и запрашиваемые разрешения.
Здесь же объявляются требования к оборудованию — Bluetooth, GPS, датчики, — настраиваются intent-фильтры (реакция на системные события и диплинки) и метаданные подключённых SDK.
Когда его нужно править:
Пример 1: объявление компонентов
Дополнительные экраны, фоновые службы и приёмники должны быть объявлены в манифесте:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!-- Здесь добавляется своя activity -->
<activity android:name=".NewScreenActivity" />
</application>
</manifest>
Пример 2: запрос разрешений
Доступ к защищённым возможностям — удержанию экрана, записи звука — объявляется ручной правкой манифеста. Свои разрешения можно добавить и через настройки разрешений во FlutterFlow.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourappname">
<!-- Разрешения -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<application
android:label=""
tools:replace="android:label"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<activity android:name=".NewScreenActivity"/>
</application>
</manifest>
Без них приложение не сможет ни держать экран включённым, ни записывать звук.
Пример 3: метаданные для SDK
Многие пакеты — Google Maps, Firebase, AdMob — требуют тег <meta-data> с настройками. Для инициализации Mapbox:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!-- Ваш фрагмент -->
<meta-data
android:name="com.example.MAPS_API_KEY"
android:value="YOUR_API_KEY" />
</application>
</manifest>
Пример 4: требования к устройству
Требование камеры, GPS или сенсорного экрана не даст установить приложение туда, где их нет:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<!-- Ваш фрагмент -->
<uses-feature android:name="android.hardware.camera" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name">
<!-- остальные компоненты -->
</application>
</manifest>
Пример 5: незашифрованные соединения
Если приложению нужен HTTP — для отладки или из-за старого сервера, — в тег <application> добавляется android:usesCleartextTraffic="true":
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:usesCleartextTraffic="true"> <!-- эта строка -->
<!-- остальные компоненты -->
</application>
</manifest>
AndroidManifest.xml правится и фрагментом, и вручную.
build.gradle (Android)
build.gradle — сценарий сборки модуля Android. Лежит в android/app/ и определяет, как приложение компилируется и упаковывается:
- версии SDK (
compileSdkVersion,minSdkVersion,targetSdkVersion); - зависимости от сторонних библиотек;
- типы сборки (отладочная и релизная);
- настройки подписи релиза;
- параметры Kotlin и Flutter;
- MultiDex и правила ProGuard;
- параметры упаковки.
Пример 1: версии SDK
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.example.myapp"
minSdkVersion 21
targetSdkVersion 33
versionCode 1
versionName "1.0"
}
}
Нужно при переходе на новый уровень API или для совместимости с конкретной библиотекой.
Пример 2: сторонние библиотеки
dependencies {
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation 'androidx.work:work-runtime:2.7.1'
}
Так подключаются Google Maps, Firebase Messaging, WorkManager.
Пример 3: правила ProGuard для релиза
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Уменьшает размер APK и затрудняет разбор кода.
Пример 4: MultiDex
Если приложение упирается в предел в 64 тысячи методов — обычная история при большом числе зависимостей:
defaultConfig {
...
multiDexEnabled true
}
Признак — ошибка сборки Too many methods.
build.gradle правится и фрагментом, и вручную.
ProGuard (Android)
Файл proguard-rules.pro управляет сжатием и обфускацией кода: уменьшает размер APK или AAB и затрудняет обратную разработку. Лежит он в android/app/proguard-rules.pro и применяется при релизной сборке с включённым сжатием.
Правила задают, что нельзя удалять и переименовывать.
Пример 1: сторонние библиотеки
Обфускация ломает библиотеки, полагающиеся на имена классов:
# Firebase
-keep class com.google.firebase.** { *; }
# Gson (сериализация JSON)
-keep class com.google.gson.** { *; }
-keepattributes *Annotation*
Без этого Firebase и Gson падают на сериализации.
Пример 2: отладка проблем ProGuard
Если в релизе приложение падает, а в отладочной сборке работает, скорее всего ProGuard вырезал нужное:
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
public static *** i(...);
}
Правило убирает отладочные записи из релиза, оставляя их в отладочной сборке.
Пример 3: убрать отладочную информацию
-dontwarn android.util.Log
Пример 4: нативные библиотеки (JNI)
-keep class com.example.native.** { *; }
-keepclassmembers class * {
native <methods>;
}
Сохраняет нативные методы нетронутыми.
Пример 5: код, использующий рефлексию
-keep class * implements android.os.Parcelable { *; }
-keepclassmembers class ** {
@android.webkit.JavascriptInterface <methods>;
}
Библиотеки, вызывающие методы через рефлексию, иначе перестают работать.
Info.plist (iOS)
Info.plist — файл настроек приложения iOS в формате XML. Он обязателен для любого приложения и лежит в /ios/Runner/.
В нём указаны идентификатор пакета, отображаемое имя, версия и, что важнее всего, тексты запросов разрешений.
Пример 1: разрешения
Доступ к местоположению во время работы приложения и в фоне. Свои разрешения можно добавить и через настройки разрешений.
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app requires location access while in use to provide location-based services.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app requires background location access to enable continuous location tracking.</string>
Пример 2: соединения по HTTP
iOS по умолчанию требует HTTPS. Для работы с сервером без него:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Настройка снимает ограничение для всех запросов сразу — пользуйтесь ей осторожно.
Пример 3: фоновые режимы
Воспроизведение звука и отслеживание местоположения в фоне:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>location</string>
</array>
Пример 4: ключи сторонних пакетов
Для карт Mapbox нужен токен доступа:
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>MGLMapboxAccessToken</key>
<string>YOUR_MAPBOX_ACCESS_TOKEN</string>
Ключ MGLMapboxAccessToken инициализирует карты, а io.flutter.embedded_views_preview со значением true разрешает встраивать нативные представления во Flutter.
Info.plist правится и фрагментом, и вручную.
Entitlements.plist (iOS)
Этот файл перечисляет привилегии приложения — доступ к сервисам Apple: iCloud, push-уведомлениям, App Groups, фоновым режимам, связке ключей. Лежит он в /ios/Runner/ под именем Runner.entitlements.
Пример 1: iCloud
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.yourcompany.appname</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
Пример 2: связка ключей
<key>keychain-access-groups</key>
<array>
<string>com.yourcompany.appname</string>
</array>
Позволяет безопасно хранить учётные данные, токены и ключи шифрования.
Пример 3: App Groups
Общее хранилище для приложения и его расширений — виджета, команды Siri:
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.yourcompany.shared</string>
</array>
Пример 4: Apple Wallet
<key>com.apple.developer.pass-type-identifiers</key>
<array>
<string>pass.com.yourcompany.appname</string>
</array>
Entitlements.plist правится и фрагментом, и вручную.
AppDelegate.swift (iOS)
AppDelegate.swift — точка входа приложения iOS: настройка окружения, события жизненного цикла (запуск, уход в фон, завершение), регистрация плагинов Flutter и инициализация SDK вроде Firebase или Branch.
Лежит в ios/Runner/AppDelegate.swift.
Пример: регистрация своего плагина
Плагины, которые не регистрируются автоматически, подключаются вручную:
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let myPlugin = CustomPlugin()
myPlugin.register(with: controller)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
AppDelegate.swift правится и фрагментом, и вручную.
main.dart (Flutter)
main.dart — точка входа приложения: файл, который выполняется первым. Он инициализирует приложение, настраивает зависимости и задаёт корневой виджет. Лежит в каталоге lib/ и содержит функцию main().
Сюда добавляют код, который должен выполниться на старте: инициализацию сторонних SDK, глобальные настройки, разовые вызовы.
Фрагменты для main.dart недоступны — только ручной режим.
Пример 1: инициализация стороннего пакета
Пакеты аналитики и отслеживания ошибок — Sentry и подобные — требуют вызова инициализации до запуска приложения:
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
Future<void> main() async {
runZonedGuarded(() async {
await SentryFlutter.init(
(options) {
options.dsn = 'https://example@sentry.io/add-your-dsn-here';
},
);
runApp(MyApp());
}, (exception, stackTrace) async {
await Sentry.captureException(exception, stackTrace: stackTrace);
});
}
Пример 2: вид строки состояния
Цвет строки состояния и яркость значков задаются до runApp():
import 'package:flutter/services.dart';
void main() {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.redAccent, // цвет строки состояния
statusBarIconBrightness: Brightness.dark, // тёмные значки на Android
statusBarBrightness: Brightness.light, // светлые значки на iOS
),
);
runApp(MyApp());
}
Пример 3: фиксация ориентации экрана
import 'package:flutter/services.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
runApp(MyApp());
}
Приложение будет работать только в альбомной ориентации.
Пример 4: отслеживание жизненного цикла
Реакция на уход приложения в фон и возврат:
import 'package:flutter/widgets.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
WidgetsBinding.instance.addObserver(AppLifecycleObserver());
runApp(MyApp());
}
class AppLifecycleObserver with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
print('App is in foreground');
} else if (state == AppLifecycleState.paused) {
print('App is in background');
}
}
}
Что стоит учесть
- Сохраняйте копию. Перед правкой нативного файла сохраните исходный текст или сделайте коммит — чтобы было куда вернуться.
- По одной правке за раз. Внесли изменение — проверили приложение. Иначе при поломке непонятно, что именно её вызвало.
- Читайте документацию пакета. Обычно там приведён готовый фрагмент — его и вставляйте. При сомнениях сверяйтесь с документацией Android или Apple: например, точное значение для фоновой загрузки (
fetchв массивеUIBackgroundModes) указано у Apple. - Ничего лишнего. Разрешения и привилегии «на всякий случай» усложняют приложение и вызывают вопросы при проверке в магазине.
- Комментируйте. Через полгода комментарий вида
<!-- Added for Payments SDK on Feb 2025 -->объяснит, откуда взялась строка. - Проверяйте на устройствах. Часть проблем с
Info.plistи привилегиями на симуляторе не видна. Правила ProGuard проявляются только в релизной сборке. - Смотрите логи. После правок следите за консолью Xcode или logcat — предупреждения о неверной настройке обычно появляются там.
- Не считайте эти файлы секретными. Всё, что лежит в
Info.plistиAndroidManifest.xml, доступно любому, кто распакует приложение. Для ключей, которые приходится включать, используйте приватные значения окружения и следите за их использованием.
Частые вопросы
Приложение не устанавливается на устройство iOS
Сверьте привилегии в `Entitlements.plist` с профилем подготовки. Ошибка «Missing entitlement» означает, что вы добавили привилегию, которой профиль не разрешает: уберите её или обновите профиль в кабинете разработчика Apple.
Ошибка «Manifest merger failed» на Android
Это конфликт в `AndroidManifest.xml` — чаще всего дублирующиеся разрешения или атрибуты, например два `
После правки `main.dart` не запускается Test Mode с включённым Supabase
Это известное ограничение. В обход используйте [**Local Run**](../../testing-deployment-publishing/running-your-app/local-run.md).
Можно ли править конфигурационные файлы в проекте-библиотеке?
Да. При подключении библиотеки её фрагменты для `AndroidManifest.xml`, `Info.plist` и `Entitlements.plist` вливаются в файлы проекта автоматически.
Более того, библиотека может передавать в эти фрагменты значения — те же ключи API — через Library Values.

Благодаря этому подключение PostHog, Sentry, CleverTap, flutter_local_notifications, flutter_nfc_kit и прочего сводится к установке библиотеки из маркетплейса.