layers_flutter 1.0.0 copy "layers_flutter: ^1.0.0" to clipboard
layers_flutter: ^1.0.0 copied to clipboard

Layers SDK for Flutter. Analytics with ATT, SKAN, and deep link integrations.

Layers Flutter SDK #

Flutter SDK for Layers analytics, built on a Rust core via dart:ffi.

Installation #

dependencies:
  layers_flutter: ^0.1.0

Usage #

import 'package:layers_flutter/layers_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Layers.init(LayersConfig(
    apiKey: 'your-api-key',
    appId: 'your-app-id',
  ));

  runApp(MyApp());
}

Track Events #

Layers.track('button_tapped', properties: {'screen': 'home'});

Screen Views #

Layers.screen('HomeScreen');

Automatic Screen Tracking #

Add LayersNavigatorObserver to your MaterialApp to track screen views automatically when routes change:

MaterialApp(
  navigatorObservers: [LayersNavigatorObserver()],
  // ...
)

Identify Users #

Layers.identify('user-123');

Set User Properties #

Layers.setUserProperties({'plan': 'premium', 'signup_date': '2025-01-15'});

Control what data the SDK collects. Set analytics and/or advertising to true or false. Null values mean "not determined".

Layers.setConsent(ConsentSettings(analytics: true, advertising: false));

ATT Integration #

Request App Tracking Transparency authorization on iOS. On Android, all ATT methods return safe defaults.

// Check if ATT is available (iOS 14.5+)
final available = await ATT.isAvailable();

if (available) {
  // Check current status before prompting
  final status = await ATT.getStatus();

  if (status == ATTStatus.notDetermined) {
    // Show the system ATT dialog
    final result = await ATT.requestAuthorization();

    if (result == ATTStatus.authorized) {
      // User granted tracking permission
      final idfa = await ATT.getAdvertisingId();
    }
  }
}

SKAN Integration #

Update SKAdNetwork conversion values on iOS. All methods are no-ops on Android.

// SKAN 2.0+: update fine conversion value (0-63)
await SKAN.updateConversionValue(42);

// SKAN 4.0: update with coarse value and optional window lock
await SKAN.updatePostbackConversionValue(
  fineValue: 42,
  coarseValue: SKANCoarseValue.high,
  lockWindow: true,
);

iOS Setup #

Add your URL scheme to Info.plist:

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myapp</string>
    </array>
  </dict>
</array>

For Universal Links, add the Associated Domains entitlement:

applinks:yourdomain.com

Android Setup #

Add intent filters to AndroidManifest.xml:

<activity ...>
  <!-- Deep link URL scheme -->
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" />
  </intent-filter>

  <!-- App Links (verified) -->
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="https" android:host="yourdomain.com" />
  </intent-filter>
</activity>

Dart Code #

// Get the deep link that launched the app (cold start)
final initialLink = await DeepLinks.getInitialLink();
if (initialLink != null) {
  handleDeepLink(initialLink);
}

// Listen for deep links while the app is running (warm start)
final unsubscribe = DeepLinks.addListener((data) {
  // data.url, data.scheme, data.host, data.path, data.queryParams
  handleDeepLink(data);
});

// Stop listening when done
unsubscribe();

Commerce Tracking #

Commerce.trackPurchase(
  productId: 'pro_monthly',
  revenue: 9.99,
  currency: 'USD',
  transactionId: 'txn_abc123',
  store: 'app_store',
  quantity: 1,
);

RevenueCat Integration #

Connect your existing RevenueCat Purchases instance to automatically track subscription events and sync user properties. No hard dependency on purchases_flutter -- uses duck typing.

import 'package:purchases_flutter/purchases_flutter.dart';

// Connect after RevenueCat is configured
RevenueCat.connect(Purchases.instance);

// Optionally track individual package purchases
final offerings = await Purchases.instance.getOfferings();
final package = offerings.current?.availablePackages.first;
if (package != null) {
  RevenueCat.trackPurchase(package);
}

Superwall Integration #

Track paywall events from your existing Superwall setup. No hard dependency on superwall_flutter -- uses duck typing.

// Track paywall presentation
Superwall.instance.setPaywallPresentationHandler(
  PaywallPresentationHandler()
    ..onPresent((info) {
      LayersSuperwall.trackPresentation(info);
    })
    ..onDismiss((info) {
      LayersSuperwall.trackDismiss(info);
    })
    ..onSkip((reason) {
      LayersSuperwall.trackSkip(null, reason.toString());
    }),
);

Error Handling #

Register an onError callback to receive SDK errors. Errors are always logged via dart:developer regardless of this callback.

Layers.onError = (error) {
  // Forward to your crash reporter, logging service, etc.
  print(error);
};

await Layers.init(LayersConfig(
  apiKey: 'your-api-key',
  appId: 'your-app-id',
));

The SDK also provides typed exceptions:

import 'package:layers_flutter/layers_flutter.dart';

// LayersException — base type with message and optional code
// LayersNotInitializedException — thrown when calling methods before init
// LayersQueueFullException — thrown when the event queue is at capacity

Debug Mode #

Enable debug logging to see SDK activity in the console and dart:developer logs:

await Layers.init(LayersConfig(
  apiKey: 'your-api-key',
  appId: 'your-app-id',
  enableDebug: true,
));

When enabled, the SDK logs track, screen, and identify calls with their arguments.

Print the full SDK state at any time:

Layers.debugPrintState();
// [Layers] === SDK Status ===
// [Layers] Initialized: true
// [Layers] Session ID: abc-123
// [Layers] Queue Depth: 5
// [Layers] User ID: user-42
// [Layers] Consent: analytics=true, advertising=unset
// [Layers] Debug Mode: true
// [Layers] Environment: production
// [Layers] App ID: your-app-id
// [Layers] Base URL: (default)
// [Layers] === End Status ===

Testing #

Use enableTestMode to inject a mock bindings implementation so tests run without the native Rust library:

import 'package:layers_flutter/layers_flutter.dart';

void main() {
  late MockLayersBindings mock;

  setUp(() {
    mock = MockLayersBindings();
    Layers.enableTestMode(mock);
  });

  tearDown(() {
    Layers.resetForTesting();
  });

  test('tracks events', () {
    Layers.track('button_tap', properties: {'screen': 'home'});
    expect(mock.trackedEvents, hasLength(1));
    expect(mock.trackedEvents.first['event'], 'button_tap');
  });
}
0
likes
135
points
--
downloads

Publisher

unverified uploader

Layers SDK for Flutter. Analytics with ATT, SKAN, and deep link integrations.

Documentation

API reference

License

MIT (license)

Dependencies

ffi, flutter, path_provider

More

Packages that depend on layers_flutter

Packages that implement layers_flutter