Skip to the page
Docs
Flutter

Docs SDKs

Version 0.x

Flutter

Pure Dart, no native code of its own. iOS and Android.

Install

Terminal
flutter pub add apparent

Android release builds need the internet permission (Flutter's template only adds it for debug):

android/app/src/main/AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>

Start it

In main(), before runApp:

Dart
import 'package:apparent/apparent.dart';

await Apparent.start(app: '<app id>', key: '<write key>', apiUrl: 'https://in.apparent.fyi');

No need to wait for it. Add disabled: kDebugMode if debug builds shouldn't send.

Screens

Dart
MaterialApp(navigatorObservers: [ApparentNavigatorObserver()], routes: {...});

Each page route is sent by its name (/settings/account → settings/account, / → home). Dialogs, sheets and unnamed routes are skipped. With go_router, pass it in GoRouter(observers: [...]). Your own names: ApparentNavigatorObserver(screenName: (route) => …), or Apparent.screen('checkout').

Events and users

Dart
Apparent.track('paywall_view', {'source': 'onboarding'});
Apparent.setProperties({'plan': 'pro'});   // on every later event; null removes one
Apparent.identify('user-42');               // after sign-in
Apparent.logout();                          // on sign-out

More in Events and users.

Purchases

Dart
await Apparent.linkRevenueCat((attrs) => Purchases.setAttributes(attrs));

Errors and opting out

  • Flutter errors and uncaught Dart errors are sent as error events. Your own handlers (Crashlytics, Sentry) still run.
  • Native iOS and Android crashes are not caught.
  • Errors you catch: Apparent.captureError(e, stack: s).
  • Apparent.optOut() / Apparent.optIn(), remembered.
  • Ask for consent first? Start with optOutByDefault: true and call Apparent.optIn() once they agree. Nothing is collected before that.
  • Nothing showing up? In a debug build the SDK prints Apparent: app id or key is wrong — events are not being saved once when the app id or key doesn't match. Copy both again from Connect.
  • Simulators and emulators say so themselves (simulator in each batch), so they're never counted, even in a release build.

Flutter can't see links or notification taps by itself, so pass them on from the package you already use.

Dart
// app_links: the link the app was launched with, then each later one
AppLinks().uriLinkStream.listen(Apparent.trackDeepLink);

// firebase_messaging: a tapped notification
final first = await FirebaseMessaging.instance.getInitialMessage();
if (first != null) Apparent.trackPushOpened(first.data, id: first.messageId);
FirebaseMessaging.onMessageOpenedApp.listen((m) => Apparent.trackPushOpened(m.data, id: m.messageId));
  • Links are sent as deep_link_opened with the link (secrets like token= or code= replaced by [redacted]) and its utm_source, utm_medium and utm_campaign. A campaign link that opens the app on its first day counts as where that person came from. Turn it off with trackDeepLinks: false.
  • Notifications are sent as push_opened with the campaign and title you put in the notification's data. Never the text people saw. Turn it off with trackPushOpens: false.
  • Your own filter. beforeSend sees every event before it is saved. Return it changed, or null to drop it:
Dart
Apparent.start(
  app: '<app id>', key: '<write key>',
  beforeSend: (event) {
    if (event.name == 'debug_tap') return null;
    event.props?.remove('email');
    return event;
  },
);

Privacy

The package has no PrivacyInfo.xcprivacy of its own (its plugins ship theirs), so add Apparent's rows to your app's. See Privacy answers.

Updated 2026-10-24 · Markdown · Report a mistake