Insider • Documentation • pub.dev • MIT License
This project demonstrates how to integrate the Insider Flutter SDK into a Flutter application on iOS and Android. One Dart codebase drives both platforms; the flutter_insider plugin brings the native Insider SDKs with it.
- On iOS; the native SDKs can be resolved with Swift Package Manager or CocoaPods.
- On Android; they come from the Insider Maven repository through Gradle.
The app includes working examples of every major SDK feature, the two iOS push notification extensions, and Firebase / Huawei push on Android.
| Requirement | Minimum |
|---|---|
| Flutter | 3.24 (Swift Package Manager support) |
| Dart | 3.0 |
| iOS | 15.6, Xcode 15 |
| Android | API 24 (Android 7.0), compile and target SDK 36 |
| Android Gradle Plugin | 8.13 (Gradle 9.1) |
| JDK | 17 |
lib/
├── main.dart # SDK initialization & callback handler
├── firebase_options.dart # Placeholder, replaced by `flutterfire configure`
├── firebase/
│ └── insider_push_bridge.dart # Routes Firebase messages to Insider or the app
├── components/ # Shared widgets
└── insider/ # One page per SDK feature
├── user_attribute.dart, user_identifier.dart
├── event.dart, product.dart, purchase.dart, wishlist.dart
├── smart_recommender.dart, content_optimizer.dart
├── page_visit.dart, gdpr.dart, geofence.dart
├── in_app_messages.dart
└── app_cards.dart, app_cards_page.dart
ios/
├── Podfile # Adds InsiderMobileAdvancedNotification to Runner in CocoaPods mode
├── Runner/ # AppDelegate, SceneDelegate, Info.plist, entitlements
├── InsiderNotificationService/ # Rich push (Service Extension)
│ └── NotificationService.swift
└── InsiderNotificationContent/ # Interactive push (Content Extension)
├── NotificationViewController.swift
└── InsiderInterface.storyboard
android/
├── build.gradle # Insider & Huawei Maven repositories, plugin classpaths
├── settings.gradle # AGP / Kotlin plugin versions
└── app/
├── build.gradle # applicationId, partner placeholder, signing
├── google-services.json # Firebase (git-ignored, you provide it)
├── agconnect-services.json # Huawei (git-ignored, you provide it)
└── src/main/
├── AndroidManifest.xml # Permissions, launcher activity
└── kotlin/.../MainActivity.kt
The two iOS notification extensions are shared by both dependency managers. They link
InsiderMobileAdvancedNotificationfromRunner.appand carry their ownInsiderInterface.storyboard, so nothing in them changes when you switch channels.
git clone git@github.com:useinsider/FlutterDemo.git
cd FlutterDemo
flutter pub getThe demo also integrates Firebase Messaging next to Insider (see Firebase Messaging alongside Insider). Point it at your own Firebase project with the FlutterFire CLI; the command overwrites the placeholder lib/firebase_options.dart with your project's values, so do not commit the result:
dart pub global activate flutterfire_cli
flutterfire configureWithout this step the app still runs; Firebase is skipped with a [FCM] Firebase not configured log and only the Insider SDK is active.
Every value you must replace is marked with a FIXME-INSIDER comment; search for that key to find them all.
Partner Name and App Group in lib/main.dart. The app group is only used on iOS, but the same call initializes both platforms:
await FlutterInsider.Instance.init(
"YOUR_PARTNER_NAME", "YOUR_APP_GROUP",
(int type, dynamic data) { /* ... */ });-
App Group identifier in all of these files. It must be identical everywhere, and the App Groups capability must be enabled for all three bundle identifiers in your Apple Developer account:
-
Signing & Capabilities for every target (
Runner+ both extensions):- Set your bundle identifiers; the extension identifiers must be prefixed with the app identifier
- Set your development team
- Enable Push Notifications
- Add an App Groups capability with the same identifier used above
- Enable Background Modes: Remote notifications, Location updates
Important: The App Group identifier must be identical across the main app target, both notification extension targets and the
initcall inlib/main.dart. A mismatch will prevent the SDK from sharing data between the app and its extensions.
- URL Scheme: In the
Runnertarget's Info tab under URL Types, set the scheme to match your partner name (e.g.,insideryourpartnername). This is what lets you register a test device from the panel with a QR code or e-mail.
Update the following values in android/app/build.gradle:
- Partner Name — the SDK reads it from the manifest placeholder; it must match the name passed to
initin Dart. This is also what lets you register a test device from the panel with a QR code or e-mail:
manifestPlaceholders = [ 'partner': 'YOUR_PARTNER_NAME' ]- Application ID — must match the package registered in your
google-services.json:
applicationId "com.your.package.name"-
Firebase — drop your
google-services.jsonintoandroid/app. The file is git-ignored. -
Huawei — drop your
agconnect-services.jsonintoandroid/app. The file is git-ignored. The build applies the AppGallery Connect plugin, so the file is required even if you do not ship to Huawei devices; alternatively remove thecom.huawei.agconnectlines fromandroid/build.gradleandandroid/app/build.gradleand use the-nh(no Huawei) variant offlutter_insiderfrom pub.dev. -
Optionally change the app label in
android/app/src/main/AndroidManifest.xml.
Choose one of the following methods. The choice is a Flutter setting; the Xcode project is the same for both.
Swift Package Manager
flutter config --enable-swift-package-manager
flutter pub getFlutter generates a Swift package that depends on the flutter_insider package, which in turn resolves the native SDKs from Insider-iOS-SDK. flutter build ios still runs pod install for the remaining CocoaPods integration; that is expected.
CocoaPods
flutter config --no-enable-swift-package-manager
flutter pub getpod install runs as part of flutter build ios. If you run it by hand, always run flutter pub get first: the Podfile reads the channel Flutter recorded in .flutter-plugins-dependencies and adds InsiderMobileAdvancedNotification to the Runner target only in CocoaPods mode.
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
pod "InsiderMobileAdvancedNotification" unless swift_package_manager_enabled?
target 'InsiderNotificationContent' do
inherit! :search_paths
end
target 'InsiderNotificationService' do
inherit! :search_paths
end
endNote: Switching channels makes
pod installadd or remove the[CP]build phases inios/Runner.xcodeproj/project.pbxproj. That diff is expected and safe to discard. After changing the native Insider SDK version, runflutter cleanand delete~/Library/Developer/Xcode/DerivedData/Runner-*.
No extra steps: the Gradle wrapper downloads itself on first build and the flutter_insider plugin declares the native SDKs (com.useinsider:insider, com.useinsider:insiderhybrid, Firebase Messaging, Play Services Location, Huawei Push / Location). The Insider and Huawei Maven repositories are already declared in android/build.gradle:
allprojects {
repositories {
google()
mavenCentral()
maven { url "https://mobilesdk.useinsider.com/android" }
maven { url "https://developer.huawei.com/repo/" }
}
}JDK note: Gradle 9.1 runs on JDK 17 or newer. If Flutter picks an older JDK, point it at Android Studio's bundled runtime:
flutter config --jdk-dir "/Applications/Android Studio.app/Contents/jbr/Contents/Home"
flutter run # on a connected device, simulator or emulator
flutter build ios --simulator # iOS, build only
flutter build apk --debug # Android, build onlyTo work from Xcode, generate the project configuration first, then open the workspace and run the Runner scheme:
flutter build ios --config-only
open ios/Runner.xcworkspaceTo work from Android Studio, open the project root with the Flutter plugin installed, or open the android folder for Gradle-only work.
The SDK is initialized once, in Dart, for both platforms. See lib/main.dart:
import 'package:flutter_insider/flutter_insider.dart';
import 'package:flutter_insider/enum/InsiderCallbackAction.dart';
Future initInsider() async {
await FlutterInsider.Instance.init(
"YOUR_PARTNER_NAME", "YOUR_APP_GROUP",
(int type, dynamic data) {
switch (type) {
case InsiderCallbackAction.NOTIFICATION_OPEN:
// Handle push notification tap
break;
case InsiderCallbackAction.TEMP_STORE_CUSTOM_ACTION:
// Handle e-commerce events
break;
default:
break;
}
});
// Show push notifications while the app is in the foreground (iOS)
FlutterInsider.Instance.setActiveForegroundPushView();
// Request the push permission (iOS prompt / Android 13+ POST_NOTIFICATIONS)
FlutterInsider.Instance.registerWithQuietPermission(false);
FlutterInsider.Instance.enableIDFACollection(true);
FlutterInsider.Instance.enableIpCollection(true);
FlutterInsider.Instance.enableCarrierCollection(true);
FlutterInsider.Instance.enableLocationCollection(true);
FlutterInsider.Instance.startTrackingGeofence();
}The SDK requires two notification extensions on iOS for full push notification support:
- Notification Service Extension — Intercepts incoming push notifications to download rich media (images, videos) before display. See
ios/InsiderNotificationService/NotificationService.swift. - Notification Content Extension — Provides an interactive carousel UI for expanded push notifications. See
ios/InsiderNotificationContent/NotificationViewController.swift.
Both extensions must share the same App Group identifier as the main app target. Refer to the source files for the complete implementation.
The Notification Content Extension's Info.plist must include the following entries:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionCategory</key>
<string>insider_int_push</string>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>0.5</real>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>InsiderInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</string>
</dict>This project keeps InsiderInterface.storyboard inside the Notification Content Extension target, so the same setup works with both Swift Package Manager and CocoaPods and nothing has to be edited after pod install.
The storyboard references NotificationViewController without a module. The Swift class is therefore declared with @objc(NotificationViewController), which keeps its Objective-C name unmangled so the storyboard can resolve it at runtime. Keep that attribute if you rename or move the class.
No messaging service code is needed in the app. The flutter_insider plugin depends on Firebase Cloud Messaging and Huawei Push and hands Insider messages to the SDK itself; you only supply the configuration files:
android/app/google-services.json— read by the Google Services plugin at build time.android/app/agconnect-services.json— read by the AppGallery Connect plugin at build time.
Both plugins are applied at the end of android/app/build.gradle:
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.huawei.agconnect'Many apps already use firebase_messaging for their own pushes. This demo shows both SDKs receiving pushes in the same app; the wiring lives in lib/firebase/insider_push_bridge.dart.
An Insider push is recognised by "source": "Insider" in the message data. Messages that carry it are handed to FlutterInsider.Instance.handleNotification; everything else stays with the app:
Future<void> routeMessage(RemoteMessage message, {required String origin}) async {
if (message.data['source'] == 'Insider') {
await FlutterInsider.Instance.handleNotification({'data': message.data});
return;
}
print('[FCM][$origin]: ${message.data}');
}
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); // calls routeMessage
FirebaseMessaging.onMessage.listen((m) => routeMessage(m, origin: 'foreground'));To trigger push open tracking for an Insider message you render yourself in the foreground, use triggerPushProcessWithNotificationData instead of handleNotification.
Keep the app delegate as the UNUserNotificationCenter delegate and let Firebase Messaging configure itself right after it, both before calling super. Flutter forwards every notification callback to all registered plugins, so the Insider SDK and Firebase Messaging both see each notification. The Firebase call is required because this app adopts the UIScene lifecycle and plugins register after didFinishLaunchingWithOptions returns:
UNUserNotificationCenter.current().delegate = self
FLTFirebaseMessagingPlugin.configureNotificationCenterDelegate()
return super.application(application, didFinishLaunchingWithOptions: launchOptions)Replacing the delegate with Firebase's own (dropping the first line) stops Insider from receiving notification callbacks.
This ordering was verified on a simulator with xcrun simctl push payloads in the foreground: the Insider SDK's notification hook fires for both Insider and non-Insider payloads with the delegate line in place, and stops firing without it. Background and terminated-state delivery depends on APNs and a configured Firebase project and was not exercised here. On iOS the background handler runs on the main Flutter engine; on Android firebase_messaging starts a separate FlutterEngine that registers all plugins, so handleNotification is available in both cases.
Two services listen for com.google.firebase.MESSAGING_EVENT: the Insider SDK's own InsiderFirebaseMessagingService and firebase_messaging's FlutterFirebaseMessagingService. Android delivers each message to a single service, chosen from the merged manifest, so check build/app/intermediates/merged_manifests/debug/AndroidManifest.xml after the first build:
- If
FlutterFirebaseMessagingServicewins, the Dart bridge above forwards Insider messages to the SDK and nothing else is needed. - If
InsiderFirebaseMessagingServicewins, Insider pushes are handled natively but the SDK drops every other message. Give Firebase's service precedence by re-declaring it inandroid/app/src/main/AndroidManifest.xmlwith a higherandroid:priorityon its intent filter (tools:node="merge"), and let the bridge do the routing.
In this demo the merged manifest lists FlutterFirebaseMessagingService first and both services share the default priority, so Firebase's service receives the messages and the bridge routes Insider ones; no manifest change is needed.
Insider features that touch OS-protected resources need both a <uses-permission> entry in the manifest and a runtime request on Android 6.0+ (API 23+). The manifest entry alone is not sufficient.
| Feature | Permission(s) | Required from |
|---|---|---|
| Push notifications (FCM/HMS) | POST_NOTIFICATIONS |
Android 13 (API 33) |
| Geofence tracking | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION |
Android 6 (API 23) |
| Background geofence | ACCESS_BACKGROUND_LOCATION |
Android 10 (API 29), separate flow |
The demo declares them in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />registerWithQuietPermission(false) in the initialization above triggers the push permission prompt. Location permissions must be requested by the app before calling startTrackingGeofence(); use a package such as permission_handler for that in your own app.
This project is licensed under the MIT License. See the LICENSE file for details.
