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},
  ],
);

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?