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
Acme Fit
Good morning, Lena
Today
Strength 30 · Day 12
‹ Home
Strength 30
6 exercises · 32 min
‹ Workout
Acme Fit Pro
Every program, offline
Yearly
$89.00
Monthly
$9.99
rabolic_flutter
- $app_openfrom_background: false
- $screenname: Home
- $screenname: Workout
- workout_startedprogram_id: strength-30
- $screenname: Checkout
- $app_backgroundforeground_ms: 48210
6 queued in a file on the device
200POST /v1/batch · 6 accepted
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: truelogs 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
ShellRouteits own observer nameExtractorandrouteFilterto rename or skip screensRabolic.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.
| Event | When | Properties |
|---|---|---|
| $app_first_open | First launch after install | |
| $app_update | First launch on a new app version | previous_version |
| $app_open | Launch, and every return to the foreground | from_background |
| $app_background | The app leaves the foreground | foreground_ms |
| $screen | Each screen, with the navigator observer | name, 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 campaignlinked_anonymous_idfrom the link’srb_aid- Strip secrets such as magic-link tokens first: the full URL is sent
// 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.
| State | Behaviour |
|---|---|
| granted | The default. Tracks and sends normally |
| pending | Keeps new events in memory only, never on disk, and sends nothing. granted sends them; denied drops them |
| denied | Drops every event and clears the queue on disk |
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.
| Limit | Value |
|---|---|
| Event name | 1–200 characters, snake_case, not starting with $ |
| Properties | 255 keys per level, nesting depth 5, keys up to 255 characters |
| One event | 32 KB (32,768 bytes of UTF-8 JSON). Larger events are dropped |
| Queue | 10,000 events on iOS and Android, 1,000 on the web. When full, the oldest go first |
| Event age | Events older than 7 days are dropped before sending |
| Batch | 500 events or 900 KB. Bodies over 1 KB are gzipped (not on the web) |
| Requests | One 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
Create a project
Sign up, name your project, and copy its publishable key.
- 2
Add an SDK
Web, React Native, Flutter or Node.js, or the HTTP API. Or run npx rabolic init.
- 3
Watch the first event arrive
It shows in live events within seconds, and your trial starts then.