Events & Screen Tracking
Track custom events and screen views in your Flutter app with the Grovs SDK
Custom events and screen tracking require plugin 3.0.0 or later.
Tracking custom events
Use track to record an event with optional properties and tags:
import 'package:grovs_flutter_plugin/grovs.dart';
final grovs = Grovs();
await grovs.track(
'checkout_completed',
properties: {'sku': 'abc', 'total': 42.0},
tags: ['shop'],
);
await grovs.track('level_complete', properties: {'level': 5, 'score': 1200});
await grovs.track('button_tap');The plugin hands the event to the native SDK, which queues it on the device and sends it in batches. Batching intervals, retention, the hold while install attribution is resolving, and the analytics session attached to each event are native behavior and are the same as in a native app: see iOS Events & Screen Tracking and Android Events & Screen Tracking.
Event name rules
An event name must not be empty and must not be one of the reserved system event names:
view, open, install, reinstall, app_open, time_spent, reactivation, user_referred, custom, screen_view
The native SDK logs and drops a rejected event. Use trackScreenView for screen views rather than track('screen_view').
Property values
Properties cross the Flutter platform channel before they reach the native SDK, so every value must be one the channel can encode: null, bool, num, String, List, or Map with string keys, nested as deep as you like. A DateTime, an enum, or any custom object fails inside the channel with an ArgumentError before anything is sent; convert such values to strings first.
On the native side, properties are capped at 8 KB of serialized JSON and tags at 20 per event. What each native SDK does with values it cannot represent is described in the platform pages linked above. The same rules apply to trackScreenView properties.
Tags
Set global tags to attach them to every subsequent event:
await grovs.setGlobalTags(['flutter', 'production']);
await grovs.setGlobalTags(null); // clearGlobal tags are merged with per-event tags by the native SDK, up to the combined cap of 20.
Tracking screen views
await grovs.trackScreenView('Checkout', properties: {'step': 2});Use this for screens that the automatic tracker cannot see, such as tabs that switch without a route change, or when you want to attach properties to a screen view. The native SDKs drop a repeat of the same screen name within one second, so a manual call right after the observer reported the same route is not double-counted.
Automatic screen tracking
Add Grovs.navigatorObserver to your MaterialApp (or CupertinoApp):
import 'package:flutter/material.dart';
import 'package:grovs_flutter_plugin/grovs.dart';
MaterialApp(
navigatorObservers: [Grovs.navigatorObserver],
routes: {
'/': (_) => const HomeScreen(),
'/product': (_) => const ProductScreen(),
},
);The observer reports a screen view when a route is pushed, when a route is replaced (for the new route), and when a route is popped (for the route revealed underneath). Only PageRoute routes count; dialogs and bottom sheets are PopupRoutes and are skipped, as are routes in a Navigator the observer is not attached to. Automatic screen views carry no properties.
How a screen name is chosen
The name is resolved in this order, and the first non-empty result wins:
- Your
screenNameExtractor, if you constructed the observer with one. - The route's
RouteSettings.name. Named routes andpushNamedcalls set this for you. - The route's runtime type, for example
MaterialPageRoute<dynamic>.
The resolved name is then translated through the screen aliases before it is sent.
The runtime-type fallback produces obfuscated names in release builds. Name your routes, or supply a screenNameExtractor, so the names in the dashboard are stable.
Custom name resolution
Grovs.navigatorObserver is a shared GrovsNavigatorObserver. To resolve names yourself, or to observe a nested Navigator, construct your own. A Flutter NavigatorObserver can be attached to one Navigator at a time, so create a separate instance for each:
import 'package:flutter/material.dart';
import 'package:grovs_flutter_plugin/grovs_navigator_observer.dart';
final observer = GrovsNavigatorObserver(
screenNameExtractor: (route) {
// Strip query parameters from names like '/product?id=42'
final name = route.settings.name;
return name?.split('?').first;
},
);
MaterialApp(navigatorObservers: [observer]);Returning null or an empty string from the extractor falls through to the route name and then the runtime type. If you use a router package that creates its own Navigator, pass the observer through that package's observers option instead of MaterialApp.navigatorObservers.
Native screen tracking is off
The plugin configures both native SDKs with automatic screen tracking disabled. The FlutterActivity and FlutterViewController hosting your app are therefore never reported as screens, and Flutter routes are counted once, by the observer. Native screens you present yourself from platform code are not tracked either; call trackScreenView for them, or track them from the native side.
setSDK(false) stops screen views along with every other kind of collection. The observer keeps calling trackScreenView, but the native SDK records nothing while disabled.
Screen name aliases
Map raw screen names to friendlier names in the dashboard:
await grovs.setScreenAliases({
'/': 'Home',
'/product': 'Product',
'MaterialPageRoute<dynamic>': 'Untitled',
});Key the map by the raw name the observer would otherwise send: the extractor result, the route name, or the runtime type. The plugin applies aliases on the Dart side to every automatic screen view, and also forwards the map to the native SDK, which syncs it to the dashboard after authentication. Manual trackScreenView calls are sent with the name you pass. The current map is available as Grovs.screenAliases.