Skip to content

flutter pub add rabolic_flutter

Analytics for your Flutter app, on iOS, Android and the web.

One Dart package, with no platform channels of its own. Add the navigator observer and every screen is tracked; launches, returns and time in the foreground are tracked for you; events wait on the device until they are sent.

  • Navigator, go_router, auto_route
  • Queue survives restarts
  • No advertising ids

Rabolic.init(RabolicConfig)

Three lines in main.dart.

Add the package, start it with your project’s publishable key, and track the events that matter to your app. You don’t have to await init: calls made while it loads keep their timestamps and are sent once it is ready.

  • Dart 3.10 and Flutter 3.38 or later
  • Pure Dart: no platform channels of its own
  • debug: true logs every call and response
  • Calls never throw into your app
import 'package:rabolic_flutter/rabolic_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Rabolic.init(const RabolicConfig(projectKey: 'rb_pub_…'));
  runApp(const MyApp());
}

// Anywhere in the app:
Rabolic.track('workout_started', properties: {'program_id': 'strength-30'});

RabolicNavigatorObserver()

Every screen, from the router you already use.

Add the observer to your navigator and each screen sends a $screen with its name. It works with Navigator, go_router and auto_route. Only page routes count, so dialogs, sheets and menus don’t.

  • Named by RouteSettings.name, a go_router name or path pattern, or an auto_route name
  • Give each ShellRoute its own observer
  • nameExtractor and routeFilter to rename or skip screens
  • Rabolic.screen() for tab changes outside a navigator
MaterialApp(
  navigatorObservers: [RabolicNavigatorObserver()],
  // …
);

trackLifecycle: true

Launches, returns and time in the app.

On iOS and Android, the SDK sends the app’s lifecycle on its own. A session starts at launch and again when the app comes back after more than 30 seconds in the background; time in the background never counts as active time.

Events are written to a queue file, so they survive restarts and offline periods, and go out in batches with retry and backoff, and whenever the app goes to the background.

Events the Flutter SDK sends automatically
EventWhenProperties
$app_first_openFirst launch after install
$app_updateFirst launch on a new app versionprevious_version
$app_openLaunch, and every return to the foregroundfrom_background
$app_backgroundThe app leaves the foregroundforeground_ms
$screenEach screen, with the navigator observername, and context.screen_name

one dart package

iOS, Android and Flutter web.

  • iOS

    Lifecycle events, screens, sessions that start at launch and after 30 seconds in the background, and a queue in a file.

  • Android

    The same as iOS. With Auto Backup, a restored install keeps its anonymous id and sends no $app_first_open.

  • Flutter web

    Screens and web sessions (30 minutes idle, or a new campaign). The queue lives in localStorage, and the IANA time zone is sent.

  • Desktop: not yet

    On macOS, Windows and Linux every call does nothing, and debug builds log one line saying so. Your app still builds and runs.

Rabolic.handleDeepLink(uri)

Know which link opened your app.

Pass every link that opens the app to the SDK. It sends $deep_link with the link’s campaign and the rb_aid your website added through appLink(). Linking the web visitor to the app user by that id isn’t built yet; identify() with the same user id joins them today.

  • utm_* and click ids become the event’s campaign
  • linked_anonymous_id from the link’s rb_aid
  • Strip secrets such as magic-link tokens first: the full URL is sent
How identity resolution works
links.dart
// Every link that opens the app (with the app_links package)
AppLinks().uriLinkStream.listen(Rabolic.handleDeepLink);

// Sends $deep_link with the url, linked_anonymous_id (from rb_aid)
// and the campaign (utm_* and click ids)

static calls on Rabolic

The calls, and consent.

Track, name screens, identify on login and reset on logout. A consent state you set at runtime is stored on the device and wins over the config on later launches.

Consent states in the Flutter SDK
StateBehaviour
grantedThe default. Tracks and sends normally
pendingKeeps new events in memory only, never on disk, and sends nothing. granted sends them; denied drops them
deniedDrops every event and clears the queue on disk
anywhere.dart
Rabolic.track('workout_started', properties: {'program_id': 'strength-30'});

// A tab change that doesn't go through a navigator
Rabolic.screen('Progress');

Rabolic.identify('usr_8f2k1x9q', traits: {'plan': 'pro'});
Rabolic.register({'app_theme': 'dark'}); // kept across launches

// Consent, stored on the device
Rabolic.setConsent(RabolicConsent.denied);
  • Never collects advertising ids, the Android id or location
  • No form fields, clipboard or fingerprints
  • No App Tracking Transparency prompt needed

limits

The numbers.

Property values may be strings, numbers, booleans, null, lists and maps. DateTime values are sent as RFC 3339 strings, enums as their name and URIs as strings.

Flutter SDK limits
LimitValue
Event name1–200 characters, snake_case, not starting with $
Properties255 keys per level, nesting depth 5, keys up to 255 characters
One event32 KB (32,768 bytes of UTF-8 JSON). Larger events are dropped
Queue10,000 events on iOS and Android, 1,000 on the web. When full, the oldest go first
Event ageEvents older than 7 days are dropped before sending
Batch500 events or 900 KB. Bodies over 1 KB are gzipped (not on the web)
RequestsOne at a time, 10 s timeout, backoff from 1 s to 5 min with jitter. Retry-After is honored

flutter sdk faq

The Flutter SDK, answered.

Which platforms does the Flutter SDK support?

iOS, Android and Flutter web, from Dart 3.10 and Flutter 3.38. On desktop (macOS, Windows, Linux) every call does nothing, and debug builds log one line saying so.

How are screens named?

By the route’s RouteSettings.name. With go_router, by the route’s name or its path pattern (/users/:id); with auto_route, by the route name (HomeRoute). Routes without a name are skipped, and only PageRoutes count, so dialogs and sheets don’t. Pass nameExtractor and routeFilter to change both.

Do I have to await Rabolic.init?

No. Calls made while the SDK is loading are kept with their original timestamps and sent once it has loaded. Awaiting it in main() before runApp is the simplest setup.

What happens when the app is offline or killed?

Events are written to a queue file on the device, so they survive restarts and offline periods, and are sent in batches when the connection is back. Queued events are also sent when the app goes to the background. Events older than 7 days are dropped.

What goes in my App Store privacy manifest?

The SDK has no native code, so it ships no PrivacyInfo.xcprivacy of its own; the plugins it uses ship theirs. In your app’s manifest, declare Product Interaction and Device ID for analytics, not used for tracking, and User ID if you call identify. NSPrivacyTracking stays false, and App Tracking Transparency isn’t needed.

What do I declare in Google Play’s Data safety form?

App activity (app interactions) and Device or other IDs (the anonymous install id), both for analytics, and User IDs if you call identify. Data is encrypted in transit, and Rabolic processes it as your service provider.

How does a web visitor become the same person in the app?

Call identify with the same user id on the website and in the app: the two devices become one person, with their earlier anonymous events. rabolic.appLink(url) and Rabolic.handleDeepLink already carry the web visitor’s anonymous id into the app as linked_anonymous_id, but Rabolic doesn’t link people by it yet.

Can I test code that tracks events?

Yes. RabolicClient is the instance behind the static calls. Create one with your own http.Client and pass it to the code under test, or route the static calls to it with Rabolic.instance.

Track your Flutter app on every platform it ships to.

Start a 3-day trial without a card. It begins with your first event, so you can set up the SDKs first.

  1. 1

    Create a project

    Sign up, name your project, and copy its publishable key.

  2. 2

    Add an SDK

    Web, React Native, Flutter or Node.js, or the HTTP API. Or run npx rabolic init.

  3. 3

    Watch the first event arrive

    It shows in live events within seconds, and your trial starts then.