trackEvent method

Future<void> trackEvent(
  1. String eventName, {
  2. Map<String, Object?> properties = const {},
})

Records an event the app performed, such as a purchase.

Queued and sent in batches alongside campaign analytics; never blocks. The name and properties are also evaluated against Digia-owned rules.

Implementation

Future<void> trackEvent(
  String eventName, {
  Map<String, Object?> properties = const {},
}) async {
  if (!_initialized) {
    _log.e('trackEvent() ignored — called before initialize()');
    return;
  }
  _log.d('trackEvent (name=$eventName, '
      'properties=${properties.keys.join(',')})');
  // An invalid call is not a signal: nothing is queued and no rule is
  // evaluated. One setup-mistake health event per launch, with a bounded
  // sample of the offending name.
  // Names are trimmed, as the server does, so both validate the same name.
  eventName = eventName.trim();
  final limits =
      _services?.appEventsService.limits ?? AppEventLimits.defaults;
  if (!isValidAppEventName(eventName, limits)) {
    final sample = eventName.length > limits.nameMaxLength
        ? eventName.substring(0, limits.nameMaxLength)
        : eventName;
    _log.w(
      'trackEvent dropped — invalid app event name',
      stage: TimelineStage.session,
      reason: TimelineReason.appEventInvalid,
      extras: {'event_name': sample},
    );
    return;
  }
  final cleaned = cleanAppEventProperties(properties, limits);
  if (cleaned.dropped.isNotEmpty) {
    // The event is kept; only the values the backend can't store go.
    _log.w(
      'trackEvent($eventName) dropped properties the backend cannot store '
      '(a bad value, a long key, or too many): ${cleaned.dropped.join(', ')}',
      stage: TimelineStage.session,
      reason: TimelineReason.appEventInvalid,
      extras: {'event_name': eventName},
    );
  }
  properties = cleaned.properties;
  final signal = EventSignal(eventName, properties);
  // Before the enqueue, so a wait cannot end while this event is in flight,
  // even when the bus queues the signal behind one being handled.
  _engine.cancelWaits(signal);
  // Started first, so the event is queued before anything a rule emits. The
  // rules do not wait for the queue write or a batch flush.
  final tracked = _services?.appEventsService.track(
    eventName,
    properties: properties,
  );
  _engine.send(signal);
  try {
    await tracked;
  } catch (error) {
    _log.e('trackEvent() analytics failed — continuing', error: error);
  }
}