### Install iOS Dependencies with CocoaPods Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Navigate to the 'ios' directory within the example app and run 'pod install' to install necessary dependencies for iOS. This step is required before running the app on iOS. ```shell cd ios pod install cd .. ``` -------------------------------- ### Navigate to the Example App Directory Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Change the current directory to the 'example' folder within the cloned Exponea Flutter SDK repository. This directory contains the example application. ```shell cd exponea-flutter-sdk/example ``` -------------------------------- ### Run the Example App on iOS Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Execute the 'flutter run' command to build and launch the example application on a connected iOS device or simulator. Ensure you have completed the CocoaPods installation step. ```shell flutter run ``` -------------------------------- ### Run the Example App on Android (GMS Flavor) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Execute the 'flutter run --flavor gms' command to build and launch the example application on a connected Android device or emulator, specifically using the Google Mobile Services (GMS) flavor. ```shell flutter run --flavor gms ``` -------------------------------- ### Enable Push Setup Check in Flutter SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-ios.md Call this method before initializing the SDK to enable the push setup self-check feature. This feature helps verify the successful setup of push notifications. ```dart ExponeaPlugin().checkPushSetup() ``` -------------------------------- ### Run the Example App on Android (HMS Flavor) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Execute the 'flutter run --flavor hms' command to build and launch the example application on a connected Android device or emulator, specifically using the Huawei Mobile Services (HMS) flavor. This is an alternative for Huawei devices without Google Play services. ```shell flutter run --flavor hms ``` -------------------------------- ### Clone the Exponea Flutter SDK Repository Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/example-app.md Clone the Exponea Flutter SDK repository from GitHub to your local machine. This is the first step to access the example application. ```shell git clone https://github.com/exponea/exponea-flutter-sdk.git ``` -------------------------------- ### Install iOS Dependencies with CocoaPods Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Navigate to the `ios` directory in your project and run `pod install` to resolve SDK dependencies for iOS. The minimum supported iOS version is 13.0. ```shell cd ios pod install ``` -------------------------------- ### Push Notification Payload Example - JSON Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-notifications.md An example of a comprehensive push notification payload structure used for custom data processing. ```json { "notification_id": 123, "url": "https://example.com/main_action", "title": "Notification title", "action": "app|browser|deeplink|self-check", "message": "Notification message", "image": "https://example.com/image.jpg", "actions": [ {"title": "Action 1", "action": "app|browser|deeplink", "url": "https://example.com/action1"} ], "sound": "default", "aps": { "alert": {"title": "Notification title", "body": "Notification message"}, "mutable-content": 1 }, "attributes": { "event_type": "campaign", "campaign_id": "123456", "campaign_name": "Campaign name", "action_id": 1, "action_type": "mobile notification", "action_name": "Action 1", "campaign_policy": "policy", "consent_category": "General consent", "subject": "Subject", "language": "en", "platform": "ios|android", "sent_timestamp": 1631234567.89, "recipient": "user@example.com" }, "url_params": {"param1": "value1", "param2": "value2"}, "source": "xnpe_platform", "silent": false, "has_tracking_consent": true, "consent_category_tracking": "Tracking consent name" } ``` -------------------------------- ### Track Purchase Event with Nested Properties Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md This example demonstrates tracking a 'purchase' event with a complex, nested structure for properties, including a list of products. It also shows the direct call to `trackEvent()` after event creation. ```dart final event = Event( name: 'purchase', properties: { 'purchase_status': "success", 'product_list': [ {'product_id': 'abc123', 'quantity': 2}, {'product_id': 'abc456', 'quantity': 1} ], 'total_price': 7.99, }, ); _plugin.trackEvent(event); ``` -------------------------------- ### Get Customer Cookie with async/await Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/basic-concepts.md Retrieves the customer cookie using async/await syntax for cleaner asynchronous error handling. Ensure the ExponeaPlugin is initialized. ```dart Future cookieLogger() async { try { final cookie = await _plugin.getCustomerCookie(); print(cookie); } catch (error) { print('Error: $error'); } } ``` -------------------------------- ### Customize App Inbox UI Styles Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/app-inbox.md Configure App Inbox UI styles using the `AppInboxStyle` class. This example shows how to customize buttons, text views, image views, and list views. ```dart _plugin.setAppInboxProvider(AppInboxStyle( appInboxButton: SimpleButtonStyle( // ButtonStyle textOverride: 'text value', textColor: 'color', backgroundColor: 'color', showIcon: true, textSize: '12px', enabled: true, borderRadius: '5px', textWeight: 'bold|normal|100..900' ), detailView: DetailViewStyle( title: TextViewStyle( // TextViewStyle visible: true, textColor: 'color', textSize: '12px', textWeight: 'bold|normal|100..900', textOverride: 'text' ), content: TextViewStyle(...), receivedTime: TextViewStyle(...), image: ImageViewStyle( // ImageViewStyle visible: true, backgroundColor: 'color' ), button: SimpleButtonStyle(), ), listView: ListScreenStyle( emptyTitle: TextViewStyle(...), emptyMessage: TextViewStyle(...), errorTitle: TextViewStyle(...), errorMessage: TextViewStyle(...), progress: ProgressBarStyle( // ProgressBarStyle visible: true, progressColor: 'color', backgroundColor: 'color' ), list: AppInboxListViewStyle( backgroundColor: 'color', item: AppInboxListItemStyle( backgroundColor: 'color', readFlag: ImageViewStyle(), receivedTime: TextViewStyle(...), title: TextViewStyle(...), content: TextViewStyle(...), image: ImageViewStyle(), ), ) ) })) ``` -------------------------------- ### Track Session Start Manually Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Call this method to manually track the start of a user session. Ensure automatic session tracking is disabled in the SDK configuration. ```dart _plugin_.trackSessionStart() ``` -------------------------------- ### Log Message: Check process canceled (customer changed) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md The segmentation data update started for the current customer but was canceled due to an `ExponeaPlugin().identifyCustomer` call for a different customer. Check your `identifyCustomer` usage. ```text Segments: Check process was canceled because customer has changed ``` -------------------------------- ### Get Customer Cookie with .then/.catchError Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/basic-concepts.md Retrieves the customer cookie using the .then and .catchError methods for handling asynchronous results and errors. Ensure the ExponeaPlugin is initialized. ```dart import 'package:exponea/exponea.dart'; final _plugin = ExponeaPlugin(); void cookieLogger() { _plugin.getCustomerCookie() .then((cookie) => print(cookie)) .catchError((error) => print('Error: $error')); } ``` -------------------------------- ### Track In-App Message Click or Close (v1.x.x) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/version-update.md Example of handling in-app message actions in older SDK versions (pre-2.x.x). This snippet demonstrates tracking clicks and closes based on the presence of a button. ```dart const overrideDefaultBehavior = false const trackActions = false final subscription = _plugin.inAppMessageActionStream(overrideDefaultBehavior: overrideDefaultBehavior, trackActions: trackActions).listen((inAppMessageAction) { if (inAppMessageAction.button != null) { _plugin.trackInAppMessageClick(inAppMessageAction.message, inAppMessageAction.button); } else { _plugin.trackInAppMessageClose(inAppMessageAction.message, interaction: inAppMessageAction.interaction); } }); ``` -------------------------------- ### Customize In-App Content Block Action Behavior Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/in-app-content-blocks.md Override default action handling for in-app content blocks by setting `overrideDefaultBehavior` to `true` and providing custom callbacks. The example shows how to call the original tracking methods for 'show', 'click', 'close', and 'error' events. ```dart InAppContentBlockPlaceholder( placeholderId: 'example_content_block', overrideDefaultBehavior: true, onMessageShown: (placeholderId, contentBlock) { //track 'show' event. _plugin.trackInAppContentBlockShown(placeholderId, contentBlock); print("Content block shown: $contentBlock"); }, onNoMessageFound: (placeholderId) { print('Content block for $placeholderId not found'); // you may set this placeholder hidden }, onActionClicked: (placeholderId, contentBlock, action) { _plugin.trackInAppContentBlockClick(placeholderId, contentBlock, action); // content block action has to be handled for given `action.url` handleUrlByYourApp(action.url); }, onCloseClicked: (placeholderId, contentBlock) { //track 'close' event. _plugin.trackInAppContentBlockClose(placeholderId, contentBlock); // placeholder may show another content block if is assigned to placeholder ID }, onError: (placeholderId, contentBlock, errorMessage) { _plugin.trackInAppContentBlockError(placeholderId, contentBlock, errorMessage); // you may set this placeholder hidden and do any fallback }, ) ``` -------------------------------- ### Initialize Exponea SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Configure and initialize the Exponea Flutter SDK with project and authorization tokens, and an optional base URL. Handles potential initialization errors. ```dart final _plugin = ExponeaPlugin(); final configuration = ExponeaConfiguration( projectToken: 'YOUR_PROJECT_TOKEN', authorizationToken: 'YOUR_API_KEY', // default baseUrl value is https://api.exponea.com baseUrl: 'YOUR_API_BASE_URL', ); _plugin.configure(configuration).catchError((error) { print('Error: $error'); return false; }); ``` -------------------------------- ### Handle Incoming Intents in Android MainActivity Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/app-links.md Add these methods to your `MainActivity.kt` to handle incoming App Links and track them with Exponea. Ensure the `ExponeaPlugin.handleCampaignIntent` method is called. ```kotlin package com.exponea.example import android.content.Intent import android.os.Bundle import com.exponea.ExponeaPlugin import io.flutter.embedding.android.FlutterActivity class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Add this call: ExponeaPlugin.Companion.handleCampaignIntent(intent, applicationContext) super.onCreate(savedInstanceState) } override fun onNewIntent(intent: Intent) { // Add this call: ExponeaPlugin.Companion.handleCampaignIntent(intent, applicationContext) super.onNewIntent(intent) } } ``` -------------------------------- ### Log Message: Skipping initial segments update (not required) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md The SDK initialization detected that all registered streams have `includeFirstLoad` set to `false`. To check segmentation data on initialization, register a stream with `includeFirstLoad` set to `true`. ```text Segments: Skipping initial segments update process as is not required ``` -------------------------------- ### Get Segmentation Data Payload Structure Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md This JSON structure represents the data payload received for each segment update. ```json { "id": "66140257f4cb337324209871", "segmentation_id": "66140215fb50effc8a7218b4" } ``` -------------------------------- ### Initialize and Listen to Multiple Segmentation Data Streams Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md Demonstrates how to initialize multiple segmentation data streams for different categories ('discovery', 'merchandising', 'content') and listen to their respective segment updates. Remember to cancel the stream subscriptions when they are no longer needed to prevent memory leaks. ```dart Future initializeSegmentationDataStreams() async { final Exponea _plugin = Exponea(); StreamSubscription? _discoverySubscription; StreamSubscription? _merchandisingSubscription; StreamSubscription? _contentSubscription; final discoveryStream = await _plugin.segmentationDataStream('discovery', includeFirstLoad: true); final merchandisingStream = await _plugin.segmentationDataStream('merchandising', includeFirstLoad: false); final contentStream = await _plugin.segmentationDataStream('content', includeFirstLoad: false); _discoverySubscription = discoveryStream.listen((segments) { print('Discovery segments: $segments'); }); _merchandisingSubscription = merchandisingStream.listen((segments) { print('Merchandising segments: $segments'); }); _contentSubscription = contentStream.listen((segments) { print('Content segments: $segments'); }); // Remember to cancel the subscriptions when they are no longer needed // _discoverySubscription?.cancel(); // _merchandisingSubscription?.cancel(); // _contentSubscription?.cancel(); } ``` -------------------------------- ### Add Swift File for iOS Compilation Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/basic-concepts.md Create a Swift file with basic Foundation import to resolve missing Swift standard libraries on iOS. This is typically done via Xcode. ```swift import Foundation ``` -------------------------------- ### Import Exponea SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Import the Exponea Flutter SDK package to use its functionalities. ```dart import 'package:exponea/exponea.dart'; ``` -------------------------------- ### Configure SDK with Token Authorization Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Initialize the Exponea Flutter SDK using token authorization by providing your API key in the authorizationToken parameter. This is the default mode for public API access. ```dart final _plugin = ExponeaPlugin(); ... final config = ExponeaConfiguration( ... authorizationToken: "YOUR_API_KEY", ... ); final configured = await _plugin.configure(config); ``` -------------------------------- ### Log Message: Skipping initial segments update (no callback) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md During SDK initialization, a reload of segmentation data was attempted without a registered stream. Register a stream before SDK initialization to check data upon startup. ```text Segments: Skipping initial segments update process for no callback ``` -------------------------------- ### Get Segmentation Data Directly Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md Retrieve segmentation data directly using the `getSegments` method. Data is loaded from cache, which is automatically refreshed if stale or empty. Set `force` to true to bypass cache and fetch directly from the server. ```dart final segments = await _plugin.getSegments('discovery'); print('Segments: Got new segments: $segments'); ``` ```dart final segments = await _plugin.getSegments('discovery', force: true); console.info('Segments: Got new segments: ' + segments); ``` -------------------------------- ### Fetch Recommendations Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/fetch-data.md Use this snippet to retrieve personalized recommendations for the current customer from an Engagement recommendation model. Ensure the `ExponeaPlugin` is initialized and available as `_plugin`. The `RecommendationOptions` object must include a valid `id` for the recommendation model. ```dart final options = RecommendationOptions( id: 'recommendation_id', fillWithRandom: true, ); _plugin.fetchRecommendations(options) .then((list) => list.forEach((recommendation) => print(recommendation.itemId))) .catchError((error) => print('Error: $error')); ``` -------------------------------- ### Enable Verbose Logging for Exponea SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md Set the SDK log level to VERBOSE for more detailed information during troubleshooting. This should be done before initializing the SDK. ```dart ExponeaPlugin().setLogLevel(LogLevel.VERBOSE) ``` -------------------------------- ### iOS Troubleshooting: AuthorizationProviderType Conformance Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Log message indicating the registered class does not conform to AuthorizationProviderType. ```text Class ExponeaAuthProvider does not conform to AuthorizationProviderType ``` -------------------------------- ### Log Message: Adding callback triggers fetch (missing stream) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md This log suggests that a stream registration initiated a data fetch, but the stream was missing during processing. Verify that streams are not closed prematurely. ```text Segments: Adding of callback triggers fetch for no callbacks registered ``` -------------------------------- ### Add Exponea Flutter SDK Dependency Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Add this to your `pubspec.yaml` file under `dependencies:` to include the Exponea Flutter SDK. You can specify a version or a version range. ```yaml dependencies: exponea: 1.6.0 ``` -------------------------------- ### Set Activity to Single Task Launch Mode Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-android.md Configure your main activity in AndroidManifest.xml to use `singleTask` launch mode to handle deep links correctly. ```xml ``` -------------------------------- ### Prefetch In-App Content Blocks Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/in-app-content-blocks.md Configure the SDK to prefetch in-app content blocks for specified placeholders to ensure they display as soon as possible. This is done by setting the `inAppContentBlockPlaceholdersAutoLoad` in the `ExponeaConfiguration`. ```dart final _plugin = ExponeaPlugin(); ... final config = ExponeaConfiguration( ... inAppContentBlockPlaceholdersAutoLoad: ['placeholder_1'], ... ); final configured = await _plugin.configure(config); ``` -------------------------------- ### Identify Customer with SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Call the `identifyCustomer()` method from the Exponea plugin, passing the created `Customer` object to associate events with a specific customer profile. ```dart _plugin.identifyCustomer(customer); ``` -------------------------------- ### Create Customer Object with Properties Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Create a `Customer` object with a hard ID and additional properties like first name, last name, and age. This is used to identify a customer in the Exponea system. ```dart final customer = Customer( ids: { 'registered': 'jane.doe@example.com', }, properties: { 'first_name': 'Jane', 'last_name': 'Doe', 'age': 32 }, ); ``` -------------------------------- ### Handle Universal Links in iOS AppDelegate Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/app-links.md Implement this method in your `AppDelegate.swift` to process incoming Universal Links and pass them to the Exponea SDK. This is required if you are not extending `ExponeaFlutterAppDelegate`. ```swift open override func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void ) -> Bool { SwiftExponeaPlugin.continueUserActivity(userActivity) return super.application(application, continue: userActivity, restorationHandler: restorationHandler) } ``` -------------------------------- ### Create PurchasedItem for Payment Tracking Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Construct a PurchasedItem object with essential payment details before tracking a payment event. Ensure all required fields like value, currency, and productId are provided. ```dart final item = PurchasedItem( value = 12.34, currency = "EUR", paymentSystem = "Virtual", productId = "handbag", productTitle = "Awesome leather handbag" ) ``` -------------------------------- ### Set SWIFT_VERSION in Podfile Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/basic-concepts.md Specify the Swift version in your Podfile to prevent compilation errors in the Exponea iOS SDK. ```ruby ENV['SWIFT_VERSION'] = '5' ``` -------------------------------- ### Enable Multidex for Android Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/README.md Enable multidex in your android/app/build.gradle file to support multiple DEX files. ```gradle android { ... defaultConfig { ... multiDexEnabled true } ``` -------------------------------- ### Log Message: Class Does Not Implement Interface Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md This log message indicates that the registered class in AndroidManifest.xml does not implement the required AuthorizationProvider interface. ```plaintext Registered class has to implement com.exponea.sdk.services.AuthorizationProvider ``` -------------------------------- ### Fetch Recommendations Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/fetch-data.md Retrieves personalized recommendations for the current customer from an Engagement recommendation model. It returns a list of Recommendation objects. ```APIDOC ## Fetch Recommendations ### Description Use the `fetchRecommendations` method to get personalized recommendations for the current customer from an Engagement recommendation model. The method returns a list of `Recommendation` objects containing the recommendation engine data and recommended item IDs. ### Method `fetchRecommendations` ### Parameters #### Arguments - **options** (RecommendationOptions) - Required - Recommendation options (see below for details). #### RecommendationOptions - **id** (String) - Required - ID of your recommendation model. - **fillWithRandom** (bool) - Optional - If true, fills the recommendations with random items until size is reached. This is utilized when models cannot recommend enough items. - **size** (int?) - Optional - Specifies the upper limit for the number of recommendations to return. Defaults to 10. - **items** (Map?) - Optional - If present, the recommendations are related not only to a customer, but to products with IDs specified in this array. Item IDs from the catalog used to train the recommendation model must be used. Input product IDs in a dictionary as `[product_id: weight]`, where the value weight determines the preference strength for the given product (bigger number = higher preference). Example: `["product_id_1": "1", "product_id_2": "2",]` - **noTrack** (bool?) - Optional - Default value: false. - **catalogAttributesWhitelist** (List?) - Optional - Returns only the specified attributes from catalog items. If empty or not set, returns all attributes. Example: `["item_id", "title", "link", "image_link"]` ### Request Example ```dart final options = RecommendationOptions( id: 'recommendation_id', fillWithRandom: true, ); _plugin.fetchRecommendations(options) .then((list) => list.forEach((recommendation) => print(recommendation.itemId))) .catchError((error) => print('Error: $error')); ``` ### Result object #### Recommendation - **engineName** (String) - Name of the recommendation engine used. - **itemId** (String) - ID of the recommended item. - **recommendationId** (String) - ID of the recommendation engine (model) used. - **recommendationVariantId** (String?) - ID of the recommendation engine variant used. - **data** (Map) - The recommendation engine data and recommended item IDs returned from the server. ``` -------------------------------- ### Set Minimum Android SDK Version Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Update your `android/app/build.gradle` file to set the `minSdkVersion` to 24 or higher. This ensures compatibility with the Exponea SDK on Android. ```gradle android { ... defaultConfig { ... minSdkVersion 24 } } ``` -------------------------------- ### Add Flutter Exponea SDK Dependency Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/README.md Add the Exponea SDK dependency to your project's pubspec.yaml file. ```yaml exponea: x.y.z ``` -------------------------------- ### Log Message: New data ignored (different customer) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md New segmentation data was fetched for a previous customer and is being ignored for the current one. This is usually not a problem, but may cause a slight delay. Check your `ExponeaPlugin().shared.identifyCustomer` usage if this occurs frequently. ```text Segments: New data are ignored because were loaded for different customer ``` -------------------------------- ### iOS Troubleshooting: Missing Provider Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Log message indicating the advanced authorization flag is enabled but no provider is found. ```text Advanced authorization flag has been enabled without provider ``` -------------------------------- ### Listen to In-App Message Actions with Default Behavior Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/in-app-messages.md Set up a listener for in-app message actions. If `overrideDefaultBehavior` is true, the SDK will not perform default actions like resolving deep links. If `trackActions` is true, click and close events are tracked automatically. Cancel the subscription when no longer needed. ```dart // If overrideDefaultBehavior is set to true, default in-app action will not be performed ( e.g. deep link ) const overrideDefaultBehavior = false; // If trackActions is set to false, click and close in-app events will not be tracked automatically const trackActions = true; final subscription = _plugin.inAppMessageActionStream(overrideDefaultBehavior: overrideDefaultBehavior, trackActions: trackActions).listen((inAppMessageAction) { print(inAppMessageAction); }); ``` -------------------------------- ### iOS Troubleshooting: NSObject Conformance Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Log message indicating the registered class does not conform to NSObject. ```text Class ExponeaAuthProvider does not conform to NSObject ``` -------------------------------- ### Configure Exponea SDK with Hot Reload Check Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/basic-concepts.md Configures the Exponea SDK only if it has not been configured already, which is crucial when using Flutter's hot reload feature. Handles potential configuration errors. ```dart Future configureExponea(ExponeaConfiguration configuration) { try { if (!await _plugin.isConfigured()) { _plugin.configure(configuration); } else { print("Exponea SDK already configured."); } } catch (error) { print('Error: $error'); } } ``` -------------------------------- ### Configure iOS App Group in ExponeaConfiguration Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-ios.md When initializing the Exponea SDK, set the 'appGroup' property within the IOSExponeaConfiguration to the app group created in Xcode. This is necessary for handling push notification delivery and rich content. ```dart import 'package:exponea/exponea.dart'; final _plugin = ExponeaPlugin(); _plugin.configure(ExponeaConfiguration( // ... ios: IOSExponeaConfiguration( appGroup: 'your app group', ), )); ``` -------------------------------- ### Log Message: Provider Not Found Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md This log message indicates that customer token authorization is enabled but no AuthorizationProvider implementation was found by the SDK. ```plaintext Advanced auth has been enabled but provider has not been found ``` -------------------------------- ### Listen to Opened Push Notifications Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-notifications.md Set up a listener for opened push notifications to handle different action types like opening the app, deeplinks, or web links. Register this listener early in your application's lifecycle. ```dart import 'package:exponea/exponea.dart'; final _plugin = ExponeaPlugin(); final subscription = _plugin.openedPushStream.listen((openedPush) { switch(openedPush.action) { case PushActionType.app: // last push directed user to your app with no link // log data defined on Exponea backend print('app - ${openedPush.data}'); break; case PushActionType.deeplink: // last push directed user to your app with deeplink print('deeplink - ${openedPush.url}'); break; case PushActionType.web: // last push directed user to web, nothing to do here print('web'); break; } }); ``` -------------------------------- ### Log Message: Skipping segments reload (no callback) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md The SDK attempted to reload segmentation data, but no stream was registered. Ensure at least one stream is registered for segments. ```text Segments: Skipping segments reload process for no callback ``` -------------------------------- ### Listen to Segmentation Data Stream Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md Use this snippet to subscribe to real-time segmentation data updates for a specific category. Set `includeFirstLoad` to true to immediately fetch data upon subscription. ```dart final _plugin = ExponeaPlugin(); // get a segmentation data stream final stream = await _plugin.segmentationDataStream('discovery', includeFirstLoad: true); // Listen to the stream for segmentation data updates stream.listen((segments) { print('Segments: Got new segments: $segments'); }); ``` -------------------------------- ### Anonymize and Switch Engagement Project Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Use the anonymize method with ExponeaConfigurationChange to switch to a different Engagement project. This will track events to a new customer record in the new project. ```dart final configChange = ExponeaConfigurationChange( project: ExponeaProject( projectToken: 'new-project-token', authorizationToken: 'new-authorization-token', ), mapping: { EventType.payment: [ ExponeaProject( projectToken: 'special-project-for-payments', authorizationToken: 'payment-authorization-token', baseUrl: 'https://api-payments.some-domain.com', ), ], }, ); _plugin.anonymize(configChange); ``` -------------------------------- ### Log Message: Customer IDs merge failed Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md The segmentation data update process failed during ID linking. Consult error logs and your `ExponeaPlugin().identifyCustomer` usage. Contact Bloomreach support if issues persist. ```text Segments: Customer IDs merge failed, unable to fetch segments ``` -------------------------------- ### Configure Exponea SDK with Default Properties Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Set default properties during the initial configuration of the Exponea Flutter SDK. These properties will be included with every tracked event unless overwritten by event-specific properties. ```dart final config = ExponeaConfiguration( projectToken: 'YOUR_PROJECT_TOKEN', authorizationToken: 'YOUR_API_KEY', baseUrl: 'YOUR_API_BASE_URL', defaultProperties: const { 'thisIsADefaultStringProperty': 'This is a default string value', 'thisIsADefaultIntProperty': 1 }, _plugin.configure(configuration) ``` -------------------------------- ### Android Authorization Provider with Token Cache Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Implement the AuthorizationProvider interface with a token cache to avoid redundant token retrievals. Token is retrieved only if the cache is null or empty. ```kotlin class ExampleAuthProvider : AuthorizationProvider { private var tokenCache: String? = null override fun getAuthorizationToken(): String? = runBlocking { if (tokenCache.isNullOrEmpty()) { tokenCache = suspendCoroutine { retrieveTokenAsync( success = {token -> done.resume(token)}, error = {error -> done.resume(null)} ) } } return@runBlocking tokenCache } } ``` -------------------------------- ### Implement FirebaseMessagingService for Push Notifications Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/version-update.md Implement the FirebaseMessagingService to handle incoming push notifications and token updates. Call `ExponeaPlugin.handleRemoteMessage` for received messages and `ExponeaPlugin.handleNewGmsToken` for new tokens. ```kotlin import android.app.NotificationManager import android.content.Context import com.exponea.ExponeaPlugin import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage class MessageService : FirebaseMessagingService() { private val notificationManager by lazy { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } override fun onMessageReceived(message: RemoteMessage) { super.onMessageReceived(message) ExponeaPlugin.handleRemoteMessage(applicationContext, message.data, notificationManager) } override fun onNewToken(token: String) { super.onNewToken(token) ExponeaPlugin.handleNewGmsToken(applicationContext, token) } } ``` -------------------------------- ### Create Customer Object with Empty Properties Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Create a `Customer` object with only the required hard ID and an empty properties map. This is useful for updating only the customer ID without changing other attributes. ```dart final customer = Customer( ids: { 'registered': 'jane.doe@example.com', }, properties: {}, ); ``` -------------------------------- ### Android Asynchronous Authorization Provider Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Implement the AuthorizationProvider interface for asynchronous token retrieval using runBlocking and suspendCoroutine. ```kotlin class ExampleAuthProvider : AuthorizationProvider { override fun getAuthorizationToken(): String? = runBlocking { return@runBlocking suspendCoroutine { retrieveTokenAsync( success = {token -> done.resume(token)}, error = {error -> done.resume(null)} ) } } } ``` -------------------------------- ### Set Log Level at Runtime Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Use this method to change the SDK's log level dynamically. Setting it to 'debug' or 'verbose' is recommended for development and debugging purposes. ```dart _plugin.setLogLevel(LogLevel.verbose); ``` -------------------------------- ### Android Authorization Provider Implementation Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Implement the AuthorizationProvider interface to provide JWT tokens for Android. This token is used for private API access. ```kotlin class ExampleAuthProvider : AuthorizationProvider { override fun getAuthorizationToken(): String? { return "eyJ0eXAiOiJKV1Q..." } } ``` -------------------------------- ### Track Payment Event with Exponea SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Use the trackPaymentEvent method to send purchased item data to Exponea. This method requires a pre-configured PurchasedItem object. ```dart ExponeaPlugin().trackPaymentEvent(item) ``` -------------------------------- ### Listen to Received Push Notifications Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-notifications.md Set up a listener for received push notifications, which is particularly useful for handling silent push notifications. This listener can be registered early in the application's lifecycle. ```dart final subscription = _plugin.receivedPushStream.listen((receivedPush) { print(receivedPush); }); ``` -------------------------------- ### Fetch Consent Categories Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/fetch-data.md Use this snippet to retrieve a list of your consent categories and their properties. This is useful for rendering consent forms. Ensure the Exponea plugin is initialized before use. ```dart _plugin.fetchConsents() .then((list) => list.forEach((consent) => print(consent))) .catchError((error) => print('Error: $error')); ``` -------------------------------- ### Track Custom Event with Properties Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/tracking.md Use this snippet to track a custom event like 'screen_view' with specific properties such as 'screen_name'. Ensure the Event object is correctly instantiated. ```dart final event = Event( name: 'screen_view', properties: { 'screen_name': "dashboard", 'other_property': 123.45, }, ); _plugin.trackEvent(event); ``` -------------------------------- ### Configure Application ID for Multiple Apps Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/setup.md Specify the 'applicationId' in the ExponeaConfiguration when your project supports multiple mobile apps to distinguish them within Engagement. ```dart final configuration = ExponeaConfiguration( ... applicationId: '', ... ) ``` -------------------------------- ### Map Event Types to Different Projects Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/configuration.md Define a mapping to track specific event types to different Exponea projects. Events are always sent to the default project and any mapped projects. ```dart projectMapping: { EventType.banner: [ ExponeaProject( projectToken: 'other-project-token', authorizationToken: 'other-auth-token', ), ], } ``` -------------------------------- ### Handle New GCM Token in MainActivity Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-android.md This code snippet shows how to manually retrieve and handle a new GCM token in your MainActivity. This is useful when the push notification token might be missing after anonymization. ```kotlin import android.os.Bundle import com.exponea.ExponeaPlugin import io.flutter.embedding.android.FlutterActivity import com.google.firebase.messaging.FirebaseMessaging class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) FirebaseMessaging.getInstance().token.addOnSuccessListener { ExponeaPlugin.handleNewGmsToken(applicationContext, it) } } } ``` -------------------------------- ### Log Message: Skipping segments update (no callback) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/segmentation.md This log indicates that an event was tracked, but no stream is registered to receive segment data. Ensure at least one stream is registered. ```text Segments: Skipping segments update process after tracked event due to no callback registered ``` -------------------------------- ### Listen for Received Push Notifications Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-android.md Implement a listener for `receivedPushStream` to display notification banners when the app is in the foreground. Remember to cancel the subscription when no longer needed. ```dart final subscription = _plugin.receivedPushStream.listen((receivedPush) { _showPushNotification(receivedPush); }); ``` -------------------------------- ### Handle In-App Message Actions (v2.x.x) Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/version-update.md Refactored in-app message action handling for SDK versions 2.x.x and higher. This snippet uses a switch statement based on the 'type' parameter to differentiate between click, close, error, and show events. ```dart const overrideDefaultBehavior = false const trackActions = false final subscription = _plugin.inAppMessageActionStream(overrideDefaultBehavior: overrideDefaultBehavior, trackActions: trackActions).listen((inAppMessageAction) { if () { switch(inAppMessageAction.type) { case InAppMessageActionType.click: _plugin.trackInAppMessageClick(inAppMessageAction.message!, inAppMessageAction.button!); break; case InAppMessageActionType.close: _plugin.trackInAppMessageClose(inAppMessageAction.message!, button: inAppMessageAction.button, interaction: inAppMessageAction.interaction ?? true); break; case InAppMessageActionType.error: // Here goes your code break; case InAppMessageActionType.show: // Here goes your code break; } } }); ``` -------------------------------- ### Enable Customer Token Authorization in Flutter SDK Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/authorization.md Enable customer token authorization by setting advancedAuthEnabled to true during SDK configuration. ```dart final _plugin = ExponeaPlugin(); ... final config = ExponeaConfiguration( ... advancedAuthEnabled: true, ... ); final configured = await _plugin.configure(config); ``` -------------------------------- ### Show Foreground Push Notifications Source: https://github.com/exponea/exponea-flutter-sdk/blob/main/Documentation/push-ios.md Override this method to customize the display of push notifications when the app is in the foreground. The default implementation shows the notification; modify the completion handler to change presentation options. ```swift override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // show notification even if the app is in the foreground if #available(iOS 14, *) { completionHandler([.banner]) } else { completionHandler([.alert]) } } ```