Flutter SDK
Official Flutter SDK for iOS, Android, and web. Auto-tracks screen views, app lifecycle events, and supports batched event delivery.
Installation
Add to your pubspec.yaml:
dependencies: retenshun: ^1.0.0
Then run:
flutter pub get
Initialization
Initialize once in your main() function before runApp():
import 'package:retenshun/retenshun.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Retenshun.init(
projectId: 'YOUR_PROJECT_ID',
apiKey: 'pk_live_YOUR_PUBLIC_KEY',
// Optional
apiUrl: 'https://your-domain.com/api/v1',
debug: true, // Console logging
autoTrackScreens: true, // Track screen views via NavigatorObserver
autoTrackLifecycle: true, // Track app_opened / app_backgrounded
flushAt: 20, // Batch size before auto-flush
flushIntervalSeconds: 30, // Periodic flush interval
);
runApp(const MyApp());
}Auto Screen Tracking
Add RetenshunObserver to your app for automatic screen tracking:
MaterialApp( navigatorObservers: [RetenshunObserver()], // ... )
This tracks screen views automatically when routes are pushed, popped, or replaced. Uses RouteSettings.name by default.
Custom Screen Name Extractor
RetenshunObserver(
screenNameExtractor: (route) {
// Custom logic to extract screen name
return route?.settings.name?.replaceAll('/', ' ').trim();
},
)Identify Users
Call on login or registration. The user ID persists across app restarts via SharedPreferences.
// On login success
Retenshun.identify('user_123', properties: {
'email': 'john@example.com',
'name': 'John Doe',
'plan': 'pro',
'createdAt': '2026-01-15',
});Track Events
// Basic event
Retenshun.track('button_clicked', properties: {
'button': 'signup_cta',
'screen': 'home',
});
// Feature usage
Retenshun.track('feature_used', properties: {
'feature': 'csv_export',
});
// Purchase
Retenshun.track('purchase', properties: {
'amount': 49.99,
'currency': 'USD',
'product_id': 'prod_1',
});Manual Screen Tracking
If you prefer manual tracking over the NavigatorObserver:
Retenshun.screen('Product Detail', properties: {
'product_id': 'prod_1',
'category': 'Electronics',
});Update User Properties
Retenshun.setUserProperties({
'plan': 'enterprise',
'company': 'Acme Inc',
'teamSize': 25,
});E-commerce Tracking
Built-in helpers with standardized event names:
// Product viewed
Retenshun.ecommerce.productViewed(
productId: 'prod_1',
name: 'Wireless Headphones',
price: 79.99,
category: 'Electronics',
);
// Added to cart
Retenshun.ecommerce.addedToCart(
productId: 'prod_1',
name: 'Wireless Headphones',
price: 79.99,
quantity: 1,
);
// Removed from cart
Retenshun.ecommerce.removedFromCart(productId: 'prod_1');
// Checkout started
Retenshun.ecommerce.checkoutStarted(
cartId: 'cart_abc',
total: 159.98,
itemCount: 2,
);
// Order completed
Retenshun.ecommerce.orderCompleted(
orderId: 'ord_123',
total: 159.98,
currency: 'USD',
products: [
{'product_id': 'prod_1', 'quantity': 2, 'price': 79.99},
],
);Push Notifications
Push notifications work automatically via OneSignal. No manual token handling needed — the SDK handles initialization, permission requests, and device registration.
1. Android Setup
Step A — Download google-services.json
Go to Dashboard → Settings → Channels → Push Notifications and enter your Android package name (e.g. com.yourcompany.yourapp). Click Download to get your google-services.json.
# Place the downloaded file here:
your_app/
android/
app/
google-services.json <-- here
build.gradle.kts
src/Already have your own Firebase project? You can use your own google-services.json instead. Just make sure your Firebase project is linked to your OneSignal app.
Step B — Add Google Services Gradle plugin
In android/settings.gradle.kts, add the plugin:
plugins {
// ... existing plugins (flutter, android, kotlin)
id("com.google.gms.google-services") version "4.4.2" apply false
}In android/app/build.gradle.kts, apply the plugin:
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services") // <-- Add this line
}2. iOS Setup
Step A — Enable Push in Xcode
- Open
ios/Runner.xcworkspacein Xcode - Select the Runner target → Signing & Capabilities
- Click + Capability → add Push Notifications
- Click + Capability again → add Background Modes → check Remote notifications
Step B — Generate an APNs Key
Apple requires a .p8 authentication key for push delivery. You only need to do this once per Apple Developer account.
- Go to Apple Developer → Certificates, Identifiers & Profiles → Keys
- Click + to create a new key
- Name it (e.g. "Push Key") and check Apple Push Notifications service (APNs)
- Click Continue → Register
- Download the
.p8file — you can only download it once! - Note the Key ID shown on the page
- Note your Team ID from Account → Membership Details
Step C — Add APNs Key to OneSignal
If you're using the default Retenshun push delivery, contact us to add your APNs key. If you're using your own OneSignal app:
- Go to your OneSignal Dashboard → Settings → Platforms → Apple iOS
- Choose .p8 Auth Key (Recommended)
- Upload the
.p8file - Enter your Key ID, Team ID, and Bundle ID (e.g.
com.yourcompany.yourapp) - Save
3. That's It — the SDK Does the Rest
Once the platform setup above is done, the Retenshun SDK handles everything automatically:
- Fetches push config from your Retenshun project
- Initializes OneSignal with your app credentials
- Requests notification permission on first launch
- Links the device to the user when you call
Retenshun.identify() - Tracks push received and opened events automatically
- Clears the user link on
Retenshun.reset()
Sending Push from Your Server
Use the Retenshun Messages API to send push notifications:
curl -X POST https://your-retenshun-domain.com/api/v1/messages \
-H "Authorization: Bearer sk_live_YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "user_123",
"channel": "push",
"content": {
"title": "Hello!",
"body": "This notification was sent via the Retenshun API."
}
}'Push API (Client-side)
// Check push status bool enabled = Retenshun.push.isEnabled; // Request permission (called automatically, but you can retry) await Retenshun.push.requestPermission(); // Opt out of push Retenshun.push.optOut(); // Opt back in Retenshun.push.optIn();
Bring Your Own OneSignal (optional)
If you already have a OneSignal account, enter your OneSignal App ID and REST API Key in Dashboard → Settings → Channels. The SDK will use your OneSignal app instead of the default Retenshun push delivery. You'll need to configure your own FCM and APNs credentials in your OneSignal dashboard.
Using Your Own Firebase Project?
If your app already uses Firebase, you can use your own google-services.json instead of downloading one from Retenshun. Both will work — the OneSignal SDK picks up the FCM token regardless of which Firebase project generated the google-services.json. Just make sure your Firebase service account is linked in your OneSignal app's platform settings.
You can also have multiple Firebase services in the same app — the google-services.json supports multiple client entries.
Logout / Reset
// On logout Retenshun.reset();
Clears the user identity, generates a new anonymous ID, and flushes pending events.
Event Batching
Events are queued and sent in batches for performance. The SDK flushes automatically when:
- The queue reaches
flushAtevents (default: 20) - Every
flushIntervalSecondsseconds (default: 30) - The app goes to background
You can also flush manually:
await Retenshun.flush();
RetenshunProvider Widget
Optional widget for accessing the SDK via BuildContext:
// Wrap your app RetenshunProvider( child: MaterialApp(...), ) // Access from widget tree final retenshun = RetenshunProvider.of(context);
Note: The provider is optional. You can use Retenshun.* static methods directly anywhere in your app.
Utility Methods
// Get current user ID (null if not identified) String? userId = Retenshun.getUserId(); // Get anonymous ID (always set) String anonId = Retenshun.getAnonymousId(); // Clean up (typically not needed) Retenshun.dispose();
Complete Example
import 'package:flutter/material.dart';
import 'package:retenshun/retenshun.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Retenshun.init(
projectId: 'proj_abc123',
apiKey: 'pk_live_your_key',
debug: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return RetenshunProvider(
child: MaterialApp(
navigatorObservers: [RetenshunObserver()],
home: const HomePage(),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Retenshun.identify('user_123', properties: {
'email': 'john@example.com',
});
},
child: const Text('Login'),
),
ElevatedButton(
onPressed: () {
Retenshun.track('button_clicked');
},
child: const Text('Track Event'),
),
ElevatedButton(
onPressed: () => Retenshun.reset(),
child: const Text('Logout'),
),
],
),
),
);
}
}