# SDK Installation Install and configure the Airship Flutter plugin for iOS and Android applications. # Install and Set Up the Flutter Plugin > Learn how to install the Airship Flutter plugin, configure iOS and Android platforms, and initialize the SDK. This guide walks you through installing the Airship Flutter plugin and configuring it for both iOS and Android platforms. ## Requirements - Flutter 3.0.2 or higher - iOS 15+ (Xcode 16+) - Android API 23+ ## Install the plugin 1. Add the Airship dependency to your package's `pubspec.yaml` file: ```yaml dependencies: airship_flutter: ^flutterPluginVersion ``` 2. Install your Flutter package dependencies by running the following in the command line at your project's root directory: `flutter pub get` 3. Import Airship into your Dart code: ```dart import 'package:airship_flutter/airship_flutter.dart'; ``` ## Initialize Airship The Airship SDK requires a single entry point called `takeOff` to initialize. Call `takeOff` in your app's `main()` function before running the app. This ensures Airship is ready before any widgets are built. ### Get your app credentials Before calling `takeOff`, you'll need your app credentials from the Airship dashboard: 1. Log in to the [Airship dashboard](https://go.airship.com/) and open your project. 1. Select the dropdown menu (▼) next to your project name, and then **Project details**. 1. Copy your **App Key** and **App Secret**. > **Important:** Airship provides separate credentials for development and production environments. The SDK automatically selects the correct credentials based on your build configuration. However, Flutter doesn't have a built-in way to detect this, so you can use the `inProduction` flag or configure based on build mode. ### Configure takeOff The following example shows how to configure and call `takeOff` in your Flutter app: ```dart import 'package:flutter/material.dart'; import 'package:airship_flutter/airship_flutter.dart'; void main() { // Ensure Flutter is initialized WidgetsFlutterBinding.ensureInitialized(); // Configure Airship var config = AirshipConfig( // Development environment credentials defaultEnvironment: ConfigEnvironment( appKey: "YOUR_DEVELOPMENT_APP_KEY", appSecret: "YOUR_DEVELOPMENT_APP_SECRET" ), // Production environment credentials (optional but recommended) productionEnvironment: ConfigEnvironment( appKey: "YOUR_PRODUCTION_APP_KEY", appSecret: "YOUR_PRODUCTION_APP_SECRET" ), // Set to true for production builds inProduction: false, // Set to true for production or use const bool.fromEnvironment('dart.vm.product') // Cloud site: Site.us for US, Site.eu for EU site: Site.us ); // Initialize Airship Airship.takeOff(config); // Run your app runApp(MyApp()); } ``` ### Configuration options Key configuration options include: * **defaultEnvironment**: Credentials for your development/staging environment * **productionEnvironment**: Credentials for your production environment (optional but recommended) * **inProduction**: Set to `true` for production builds, `false` for development. You can use `const bool.fromEnvironment('dart.vm.product')` to automatically detect release builds * **site**: Set to `Site.us` for US cloud projects or `Site.eu` for EU cloud projects For additional configuration options (such as URL allowlists), see [Advanced Configuration](https://www.airship.com/docs/developer/sdk-integration/flutter/installation/advanced-configuration/). > **Note:** Once `takeOff` is called, the config is stored and applied for future app sessions. If you call `takeOff` again with a different config, the new config will not take effect until the next app restart. ## Test the integration After completing the setup, verify your integration to ensure everything is working correctly. 1. **Build and run your app** on both iOS and Android (if applicable): `flutter run` 2. **Check the console logs** for Airship initialization: * Look for log messages indicating successful SDK initialization * You should see a **Channel ID** in the logs—this is the unique identifier for the device Example log output: ``` Airship takeOff succeeded Channel ID: 01234567-89ab-cdef-0123-456789abcdef ``` 3. **Verify in the Airship dashboard**: 1. Open your project. 1. Go to **Audience**, then **Contact Management**. 1. Search for the Channel ID from your logs. The channel should appear with the correct platform, iOS or Android. If you don't see a channel ID or encounter errors during initialization, see the [Troubleshooting](https://www.airship.com/docs/developer/sdk-integration/flutter/troubleshooting/) guide for common problems and solutions. ## Next steps Now that you've successfully integrated the Airship SDK: 1. [**Enable push notifications**](https://www.airship.com/docs/developer/sdk-integration/flutter/push-notifications/getting-started/) and configure notification handling 2. [**Identify users**](https://www.airship.com/docs/developer/sdk-integration/flutter/audience/contacts/) with contacts and named users 3. [**Set up Message Center**](https://www.airship.com/docs/developer/sdk-integration/flutter/message-center/getting-started/) for persistent messaging 4. [**Configure analytics**](https://www.airship.com/docs/developer/sdk-integration/flutter/data-collection/analytics/) to track user behavior If you don't see a channel ID or encounter errors during initialization, see [Troubleshooting Initialization](https://www.airship.com/docs/developer/sdk-integration/flutter/troubleshooting/initialization/) for common problems and solutions. # Logging > Configure log levels and privacy settings to control how the Airship SDK logs messages. The Airship SDK provides configurable log levels to help you debug issues without overwhelming the console. By default, the log level is set to **Info** for development builds and **Error** for production builds to ensure clean logs in a live environment. ## Log levels The following log levels are available, ordered from most to least verbose. | Log Level | Description | | :-------- | :---------- | | **Verbose** | Reports highly detailed SDK status, which is useful for deep debugging and troubleshooting. | | **Debug** | Reports general SDK status with more detailed information than `Info`. | | **Info** | Reports general SDK status and lifecycle events. | | **Warning** | Used for API deprecations, invalid setup, and other potentially problematic situations that are generally recoverable. | | **Error** | Used for critical errors, exceptions, and other situations that the SDK cannot gracefully handle. | | **None** | Disables all logging. | ## Configure log levels You can set the log level in the Airship config when calling `takeOff`. This setting acts as a minimum threshold—only logs at that level and higher will be output. ```dart import 'package:flutter/material.dart'; import 'package:airship_flutter/airship_flutter.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); var config = AirshipConfig( defaultEnvironment: ConfigEnvironment( appKey: "YOUR_APP_KEY", appSecret: "YOUR_APP_SECRET" ), site: Site.us, // Set log level for both platforms logLevel: LogLevel.verbose, // Optional: Set platform-specific log privacy levels iosConfig: IOSConfig( logPrivacyLevel: IOSLogPrivacyLevel.public ), androidConfig: AndroidConfig( logPrivacyLevel: AndroidLogPrivacyLevel.public ) ); Airship.takeOff(config); runApp(MyApp()); } ``` ### Recommended log levels by environment | Environment | Recommended Level | Purpose | | :---------- | :--------------- | :------ | | **Development** | `LogLevel.verbose` or `LogLevel.debug` | Maximum detail for debugging | | **Staging/QA** | `LogLevel.info` | General status information | | **Production** | `LogLevel.error` or `LogLevel.warning` | Only critical issues | ### Example: Conditional logging Set different log levels based on build mode: ```dart import 'package:flutter/foundation.dart'; var config = AirshipConfig( // ... other config ... // Use verbose logging in debug builds, errors only in release logLevel: kDebugMode ? LogLevel.verbose : LogLevel.error, iosConfig: IOSConfig( logPrivacyLevel: kDebugMode ? IOSLogPrivacyLevel.public : IOSLogPrivacyLevel.private ), androidConfig: AndroidConfig( logPrivacyLevel: kDebugMode ? AndroidLogPrivacyLevel.public : AndroidLogPrivacyLevel.private ) ); ``` ## Log privacy levels For better security in production environments, you can control the visibility of log contents using privacy levels. This is especially useful when you need to debug a release build without exposing sensitive information like channel IDs, named users, or custom event data. ### Private (default) This is the default setting, designed to protect data in a production environment. Sensitive information is redacted from log output. **Use case**: Production builds where log output might be collected or viewed by support teams. ### Public This setting increases log visibility, making it easier to capture detailed information from release builds. Sensitive information is visible in logs. To ensure visibility in production builds, `verbose` and `debug` messages are automatically elevated to the `info` log level. **Use case**: Development builds or when actively debugging issues in test environments. > **Warning:** Only use `public` privacy level in development or controlled test environments. Never use it in production builds that will be distributed to end users, as it may expose sensitive user data in logs. ## Viewing logs View Airship SDK logs in your platform's native development tools. ### iOS logs View iOS logs in Xcode's console: 1. Run your app in Xcode 2. Open the **Debug area** (View → Debug Area → Show Debug Area or ⇧⌘Y) 3. Filter logs by typing "Airship" in the search field ### Android logs View Android logs using Logcat: 1. Run your app in Android Studio 2. Open **Logcat** (View → Tool Windows → Logcat) 3. Filter logs by selecting your app's package or searching for "Airship" Or use the command line: `adb logcat | grep Airship` ## Example log output With verbose logging enabled, you'll see detailed output like: ``` [Airship] Airship takeOff succeeded [Airship] Channel ID: 01234567-89ab-cdef-0123-456789abcdef [Airship] Push notifications enabled: true [Airship] Analytics tracking event: screen_viewed ``` ## Troubleshooting logging issues If you're not seeing expected log output: * **Verify log level**: Ensure you've set an appropriate log level (e.g., `LogLevel.verbose` for debugging) * **Check privacy level**: If using release builds, set privacy level to `public` for full visibility * **Filter console output**: Use "Airship" as a filter keyword in your IDE's console * **Restart the app**: Log configuration is set during `takeOff`, so restart the app after making changes # Locale > Configure locale behavior and override the default locale that Airship uses for messaging and analytics. The Airship SDK is localized in 48 different languages for all strings included within the SDK. By default, the SDK automatically uses the device's or app's configured [Locale](https://www.airship.com/docs/reference/glossary/#locale). Airship uses the locale for various locale-sensitive operations: * **Message localization**: Selecting the appropriate language variant for push notifications, in-app messages, and Message Center content * **Analytics reporting**: Recording the user's locale for segmentation and reporting * **SDK strings**: Displaying SDK-generated UI elements in the user's language Apps can override the locale to use a different locale than the device's current setting. This is useful when your app provides its own language selector or needs to match Airship's locale to your app's locale setting. ## Override the locale You can override the locale programmatically at runtime, which takes precedence over the device's locale settings. Use standard locale identifiers (e.g., "en", "en-US", "de", "fr-CA"). ```dart // Set locale to German Airship.locale.setLocaleOverride("de"); // Set locale to Canadian French Airship.locale.setLocaleOverride("fr-CA"); // Set locale to US English Airship.locale.setLocaleOverride("en-US"); ``` > **Note:** Locale overrides persist across app sessions. Once set, the override remains active until explicitly cleared or changed. ## Clear the locale override To remove a locale override and return to using the device's locale: ```dart Airship.locale.clearLocaleOverride(); ``` ## Get the current locale To retrieve the locale that Airship is currently using: ```dart var locale = await Airship.locale.locale; debugPrint("Current Airship locale: $locale"); ``` ## Example: Sync with app language If your app provides a language selector, you can sync Airship's locale with the user's selection: ```dart // When user changes language in your app void onLanguageChanged(String languageCode) { // Update your app's locale // ... your app-specific code ... // Update Airship's locale to match Airship.locale.setLocaleOverride(languageCode); debugPrint("Language changed to: $languageCode"); } // When user resets to device default void onResetToDeviceLanguage() { // Clear Airship's locale override Airship.locale.clearLocaleOverride(); debugPrint("Reset to device locale"); } ``` # Advanced Configuration > Configure URL allowlists and other advanced settings for the Airship Flutter SDK. This guide covers advanced configuration options for the Airship Flutter SDK. ## URL Allowlist The URL allowlist is a security feature that controls which URLs the Airship SDK can open or interact with. This prevents malicious or unintended URLs from being accessed through your app. The SDK divides URL usage into three different config options: * **urlAllowListScopeOpenUrl**: Controls URLs that can be opened from actions, landing pages, HTML in-app messages, or In-App Automation media. By default, allows Airship-originated URLs and YouTube URLs. * **urlAllowListScopeJavaScriptInterface**: Controls which URLs can have the Airship JavaScript interface injected into webviews. By default, allows only Airship-originated URLs. * **urlAllowList**: Applies both scopes to the specified URLs. ### URL pattern syntax ```text := '*' | '://'/ | '://' | ':/' | ':///' := := '*' | '*.' | := ``` ### Example allowlist configurations ```dart var config = AirshipConfig( defaultEnvironment: ConfigEnvironment( appKey: "YOUR_APP_KEY", appSecret: "YOUR_APP_SECRET" ), site: Site.us, // Allow all URLs (use with caution in production) urlAllowList: ["*"] ); ``` ```dart // Allow specific domains var config = AirshipConfig( defaultEnvironment: ConfigEnvironment( appKey: "YOUR_APP_KEY", appSecret: "YOUR_APP_SECRET" ), site: Site.us, urlAllowList: [ "https://yourwebsite.com", "https://*.yourwebsite.com/*" // All subdomains ] ); ``` ```dart // Allow specific URL patterns with different scopes var config = AirshipConfig( defaultEnvironment: ConfigEnvironment( appKey: "YOUR_APP_KEY", appSecret: "YOUR_APP_SECRET" ), site: Site.us, urlAllowListScopeOpenUrl: [ "https://yourwebsite.com/*", "https://youtube.com/*", "yourapp://*" // Deep link scheme ], urlAllowListScopeJavaScriptInterface: [ "https://yourwebsite.com/*" ] ); ``` > **Warning:** Setting `urlAllowList` to `["*"]` allows the SDK to open any URL. While convenient for development, this should be carefully considered for production environments. Only include specific domains you trust. > **Note:** These config options are passed to `Airship.takeOff()` during SDK initialization. See the [Flutter Setup guide](https://www.airship.com/docs/developer/sdk-integration/flutter/installation/getting-started/) for complete initialization instructions. # Extend Airship > How to extend the Airship Flutter plugin to access native iOS and Android SDK features not exposed through the Flutter API. You can provide a plugin extender that will be automatically loaded for the app. The extender can be used to modify the Airship config before SDK initialization and to access the underlying native SDK once Airship is ready. This gives the app a chance to customize parts of Airship that are not configurable through the Flutter plugin, such as setting up [iOS Live Activities](https://www.airship.com/docs/developer/sdk-integration/flutter/live-activities/) and [Android Live Updates](https://www.airship.com/docs/developer/sdk-integration/flutter/live-updates/). ## iOS For iOS, create a Swift file named `AirshipPluginExtender.swift` and needs to be included in the main app target. Make sure the class has the `@objc(AirshipPluginExtender)` annotation and inherits `AirshipPluginExtenderProtocol`. ```swift import Foundation import AirshipKit import AirshipFrameworkProxy import ActivityKit @objc(AirshipPluginExtender) public class AirshipPluginExtender: NSObject, AirshipPluginExtenderProtocol { public static func onAirshipReady() { // Called when Airship is ready on the MainActor } public static func extendConfig(config: inout AirshipConfig) { // Called to extend the AirshipConfig before SDK initialization } } ``` ## Android Create a file in the App's src directory named `AirshipExtender`. It needs to extend `com.urbanairship.android.framework.proxy.AirshipPluginExtender` and have an empty constructor. ```kotlin // Replace with your package package com.example import android.content.Context import androidx.annotation.Keep import com.urbanairship.AirshipConfigOptions import com.urbanairship.UAirship import com.urbanairship.android.framework.proxy.AirshipPluginExtender @Keep public final class AirshipExtender: AirshipPluginExtender { override fun onAirshipReady(context: Context, airship: UAirship) { // Called when Airship is ready on a background thread. // Avoid doing long running, blocking work or it will delay Airship } override fun extendConfig( context: Context, configBuilder: AirshipConfigOptions.Builder ): AirshipConfigOptions.Builder { // Called to extend the AirshipConfig before SDK initialization return configBuilder } } ``` Register the extender in the manifest: ```xml ```