> ## Documentation Index
> Fetch the complete documentation index at: https://docs.insito.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle events

> Subscribe to MicroSurvey lifecycle events to wire analytics, debugging, and side effects.

The SDK emits 10 lifecycle events via a small pub/sub bus. Subscribe with
`MicroSurvey.on(event, listener)`; the call returns an **unsubscribe function**.

```dart theme={null}
final off = MicroSurvey.on(InsitoEvent.responseSubmitted, (payload) {
  analytics.track('survey_response_submitted', {
    'surveyId': payload.surveyId,
    'responseId': payload.responseId,
  });
});

// when you no longer need the listener:
off();
```

Events are identified by the `InsitoEvent` enum and every listener receives an
`InsitoEventPayload`. Only the fields relevant to the fired event are populated;
the rest are `null`.

## Event matrix

| `InsitoEvent`       | Populated payload fields  | When                                                |
| ------------------- | ------------------------- | --------------------------------------------------- |
| `initialized`       | `apiUrl`                  | `init()` finished                                   |
| `surveyShown`       | `surveyId`                | Modal mounted, first question rendered              |
| `surveyDismissed`   | `surveyId`                | User tapped close (no response submitted)           |
| `surveyCompleted`   | `surveyId`                | Final question submitted, modal closing             |
| `responseSubmitted` | `surveyId`, `responseId`  | Server accepted the response                        |
| `responseQueued`    | `surveyId`, `queueLength` | Network failed, response saved locally              |
| `responseFailed`    | `surveyId`, `reason`      | Server rejected with a 4xx (validation, plan limit) |
| `screenTracked`     | `screenName`              | A new screen path was recorded                      |
| `screenMapFlushed`  | `screens`                 | The 60s timer flushed accumulated screen visits     |
| `error`             | `where`, `message`        | Any SDK error not caught by a more specific event   |

## `InsitoEventPayload`

```dart theme={null}
class InsitoEventPayload {
  final String? apiUrl;
  final String? surveyId;
  final String? responseId;
  final int? queueLength;
  final String? screenName;
  final Map<String, int>? screens;
  final String? reason;
  final String? where;
  final String? message;
}
```

## When events fire

```mermaid theme={null}
sequenceDiagram
  participant App
  participant SDK
  participant API

  App->>SDK: init()
  SDK-->>App: initialized
  App->>SDK: identify()
  App->>SDK: trigger("checkout_completed")
  SDK->>API: POST /v1/sdk/event
  API-->>SDK: { survey }
  SDK-->>App: surveyShown
  Note over App: User answers questions
  SDK->>API: POST /v1/sdk/response
  API-->>SDK: { responseId }
  SDK-->>App: surveyCompleted + responseSubmitted
```

## Common patterns

### Fan out to your own analytics

```dart theme={null}
final subscriptions = <void Function()>[
  MicroSurvey.on(InsitoEvent.surveyShown, (p) {
    analytics.track('insito_survey_shown', {'surveyId': p.surveyId});
  }),
  MicroSurvey.on(InsitoEvent.responseSubmitted, (p) {
    analytics.track('insito_response_submitted', {'surveyId': p.surveyId});
  }),
];

// on teardown:
for (final off in subscriptions) {
  off();
}
```

### Surface errors in development

```dart theme={null}
if (kDebugMode) {
  MicroSurvey.on(InsitoEvent.error, (p) {
    debugPrint('[insito:${p.where}] ${p.message}');
  });
}
```

### Show a thank-you snackbar

```dart theme={null}
MicroSurvey.on(InsitoEvent.responseSubmitted, (_) {
  messengerKey.currentState?.showSnackBar(
    const SnackBar(content: Text('Thanks for the feedback!')),
  );
});
```

### Differentiate online vs queued

```dart theme={null}
MicroSurvey.on(InsitoEvent.responseSubmitted, (p) {
  analytics.track('survey_submitted_online', {'surveyId': p.surveyId});
});
MicroSurvey.on(InsitoEvent.responseQueued, (p) {
  analytics.track('survey_submitted_offline', {'queueLength': p.queueLength});
});
```

## Memory model

Listeners are held by reference. The bus doesn't weak-ref them; if you don't
call the returned unsubscribe function, the listener (and anything it closes
over) lives for the lifetime of the app. If you subscribe from a `StatefulWidget`,
store the unsubscribe callbacks and call them in `dispose()`.

```dart theme={null}
class _MyWidgetState extends State<MyWidget> {
  late final void Function() _off;

  @override
  void initState() {
    super.initState();
    _off = MicroSurvey.on(InsitoEvent.surveyShown, _track);
  }

  @override
  void dispose() {
    _off(); // clean teardown
    super.dispose();
  }
}
```

<Note>
  Listener errors are swallowed inside the bus, so a misbehaving handler can
  never crash the SDK — but it also means exceptions won't surface. Log inside
  your listener if you need visibility.
</Note>
