Skip to Content

Events

Flutter has no per-event on(event, callback)/off(event, callback) like web and the natives. Instead, addEventListener takes a single listener object with five typed callback fields covering every SDK lifecycle event, and removeEventListener removes that whole object — there is no per-event subscription on Flutter.

final listener = OneSygnalEventListener( onReady: () { print('SDK ready'); }, onSurveyShown: (event) { print('Survey shown: ${event.surveyId}'); }, onSurveyCompleted: (event) { print('Survey completed: ${event.surveyId}'); }, onSurveyDismissed: (event) { print('Survey dismissed: ${event.surveyId}'); }, onQuestionAnswered: (event) { print('Question ${event.questionId} answered "${event.answer}" ' '(${event.languageCode}) in survey ${event.surveyId}'); }, ); OneSygnal().addEventListener(listener); // later: OneSygnal().removeEventListener(listener);

OneSygnalEventListener (in onesygnal_event_listener.dart) is a closure-based convenience class — every field is an optional named closure, so you only need to pass the ones you care about. Its five fields:

FieldType
onReadyvoid Function()?
onSurveyShownvoid Function(SurveyShownEvent event)?
onSurveyCompletedvoid Function(SurveyCompletedEvent event)?
onSurveyDismissedvoid Function(SurveyDismissedEvent event)?
onQuestionAnsweredvoid Function(QuestionAnsweredEvent event)?

If you’d rather subclass than pass closures, OneSygnalEventListenerInterface is the abstract base OneSygnalEventListener implements — every method has a no-op default, so you only override what you need:

class MyListener extends OneSygnalEventListenerInterface { @override void surveyShown(SurveyShownEvent event) { print('Survey shown: ${event.surveyId}'); } }

You can register addEventListener before initialize() has even run — subscribing just attaches to the event stream, it doesn’t require the SDK to already be running. Events start arriving once initialize() completes.

Event payloads

EventDart callbackPayload classFields
readyonReady—none
survey:shownonSurveyShownSurveyShownEventsurveyId (String)
survey:completedonSurveyCompletedSurveyCompletedEventsurveyId (String)
survey:dismissedonSurveyDismissedSurveyDismissedEventsurveyId (String)
survey:question_answeredonQuestionAnsweredQuestionAnsweredEventsurveyId (String), questionId (String), answer (dynamic, shape depends on question type, null if an optional question was left unanswered), languageCode (String, the language the survey was rendered in)

These are the same event names the natives emit, wrapped in typed Dart classes.

No survey:step

Web additionally has a survey:step event ({ surveyId, questionId, step }, fired once per answered question, with a step index). Flutter has no equivalent: the bridge only forwards the native survey:question_answered event, which has no step field — web’s survey:step has no channel wire equivalent to bridge. See web’s events page if you’re sharing analytics-forwarding code across platforms and need to know this payload doesn’t carry a step index here.

Multiple listeners

You can call addEventListener more than once — every registered listener receives every event. The bridge only subscribes to the native event channel while at least one Dart listener is registered (lazily, on the first addEventListener; torn down on the last removeEventListener), not for the app’s whole lifetime.

See Platform Parity for how these events compare across web, Android, and iOS.