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

  1. Open ios/Runner.xcworkspace in Xcode
  2. Select the Runner target → Signing & Capabilities
  3. Click + Capability → add Push Notifications
  4. 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.

  1. Go to Apple DeveloperCertificates, Identifiers & ProfilesKeys
  2. Click + to create a new key
  3. Name it (e.g. "Push Key") and check Apple Push Notifications service (APNs)
  4. Click ContinueRegister
  5. Download the .p8 file — you can only download it once!
  6. Note the Key ID shown on the page
  7. Note your Team ID from AccountMembership 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:

  1. Go to your OneSignal DashboardSettingsPlatformsApple iOS
  2. Choose .p8 Auth Key (Recommended)
  3. Upload the .p8 file
  4. Enter your Key ID, Team ID, and Bundle ID (e.g. com.yourcompany.yourapp)
  5. 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 flushAt events (default: 20)
  • Every flushIntervalSeconds seconds (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'),
            ),
          ],
        ),
      ),
    );
  }
}

What's Next?