# Events & screen tracking

> Track custom events and screen views in your Flutter app with the Grovs SDK.

Source: https://docs.grovs.io/docs/sdk/flutter/analytics

Requires plugin **3.0.0** or later.

## Tracking custom events

`track` records an event with optional properties and tags:

```dart
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 native SDK queues the event on the device and sends it in batches. Batching, retention, the hold during install attribution and analytics sessions behave as in a native app. See [iOS](https://docs.grovs.io/docs/sdk/ios/analytics) and [Android](https://docs.grovs.io/docs/sdk/android/analytics) Events & Screen Tracking.

### Event name rules

Names can't be empty or a reserved system name:

`view`, `open`, `install`, `reinstall`, `app_open`, `time_spent`, `reactivation`, `user_referred`, `custom`, `screen_view`

The native 3.1 SDKs also refuse the names Grovs uses for push and in-app reporting: `in_app_viewed`, `in_app_clicked`, `in_app_dismissed`, `in_app_answered`, `in_app_commented`, `in_app_held`, `push_open`, `push_delivered`, `push_held`, `journey_entered`, `journey_held` and `journey_path`.

The native SDK logs and drops rejected events. Use [`trackScreenView`](#tracking-screen-views) for screen views, not `track('screen_view')`.

### Property values

Properties cross the Flutter platform channel, so values must be channel-encodable: `null`, `bool`, `num`, `String`, `List`, or `Map` with string keys, nested to any depth. A `DateTime`, enum or custom object throws `ArgumentError` in the channel before anything is sent; convert it to a string first.

Natively, properties cap at **8 KB** of serialized JSON and tags at **20** per event. The platform pages above cover values each SDK can't represent. The same rules apply to `trackScreenView` properties.

### Tags

Global tags attach to every later event:

```dart
await grovs.setGlobalTags(['flutter', 'production']);

await grovs.setGlobalTags(null); // clear
```

The native SDK merges global and per-event tags, up to 20 combined.

## Tracking screen views

```dart
await grovs.trackScreenView('Checkout', properties: {'step': 2});
```

Use it for screens the automatic tracker can't see (tabs that switch without a route change) or to attach properties. The native SDKs drop a repeat of the same name within one second, so a manual call right after the observer reports the same route isn't double-counted.

## Automatic screen tracking

Add `Grovs.navigatorObserver` to your `MaterialApp` (or `CupertinoApp`):

```dart
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 on push, on replace (the new route) and on pop (the route revealed). Only `PageRoute`s count. Dialogs and bottom sheets are `PopupRoute`s and are skipped, as are routes in a `Navigator` the observer isn't attached to. Automatic screen views carry no properties.

### How a screen name is chosen

The first non-empty result wins, then goes through [screen aliases](#screen-name-aliases):

1. Your `screenNameExtractor`, if the observer has one.
2. The route's `RouteSettings.name`. Named routes and `pushNamed` set it.
3. The route's runtime type, for example `MaterialPageRoute<dynamic>`.

> **Warning:** The runtime-type fallback is obfuscated in release builds. Name your routes or supply a `screenNameExtractor` for stable dashboard names.

### Custom name resolution

`Grovs.navigatorObserver` is a shared `GrovsNavigatorObserver`. To resolve names yourself or observe a nested `Navigator`, construct your own. An observer attaches to one `Navigator` at a time, so create one per navigator:

```dart
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 falls through to the route name, then the runtime type. With a router package that creates its own `Navigator`, pass the observer through that package's observers option, not `MaterialApp.navigatorObservers`.

### Native screen tracking is off

The plugin disables native automatic screen tracking. The hosting `FlutterActivity` and `FlutterViewController` are never reported, and the observer counts Flutter routes once. Native screens you present from platform code aren't tracked; call `trackScreenView` for them or track them natively.

Screen views from the observer and `trackScreenView` are also what [in-app messages](https://docs.grovs.io/docs/sdk/flutter/in-app-messages#triggers) trigger on and what excluded screens match.

`setSDK(false)` stops screen views with all other collection. The observer still calls `trackScreenView`, but nothing is recorded while disabled.

## Screen name aliases

Map raw screen names to friendlier dashboard names:

```dart
await grovs.setScreenAliases({
  '/': 'Home',
  '/product': 'Product',
  'MaterialPageRoute<dynamic>': 'Untitled',
});
```

Key by the raw name the observer would send: extractor result, route name or runtime type. The plugin applies aliases in Dart to every automatic screen view and forwards the map to the native SDK, which syncs it after authentication. Manual `trackScreenView` calls send the name you pass. Read the current map from `Grovs.screenAliases`.

## Next steps

- [Track revenue and purchases →](https://docs.grovs.io/docs/sdk/flutter/revenue)
- [Gate collection on user consent →](https://docs.grovs.io/docs/sdk/flutter/privacy)
- [Full API reference →](https://docs.grovs.io/docs/sdk/flutter/api-reference)
