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

# Flutter Surveys & Feedback

> Collect NPS, CSAT, and custom feedback with 8 question types

<Info>
  **Supported on:** iOS SDK `1.0.61+` · Android SDK `1.0.33+` · Flutter SDK `1.0.3+`
</Info>

The surveys module lets you collect user feedback through in-app surveys. Surveys can trigger automatically based on events (like in-app messages) or be presented manually from your code.

## How It Works

Surveys are configured in the Console with content, question types, and trigger rules. Like in-app messages, they can appear automatically when trigger conditions are met -- no code required for trigger-based surveys.

## Present a Survey Manually

```dart theme={null}
await AppDNA.surveys.present("nps_q1_2026");
```

`present` returns once the survey request has been dispatched to the native SDK. Whether the survey actually appears depends on the survey's status, audience, and trigger rules configured in the Console -- the SDK fires the `survey_shown` analytics event (and your delegate's `onSurveyPresented` callback) when it does. If you need to confirm that a survey was shown to the user, observe the delegate rather than the return value.

## Module Access

```dart theme={null}
final surveys = AppDNA.surveys;
```

### Module Methods

| Method        | Signature                                 | Description                                   |
| ------------- | ----------------------------------------- | --------------------------------------------- |
| `present`     | `Future<void> present(String surveyId)`   | Request that the SDK present a survey         |
| `setDelegate` | `void setDelegate(AppDNASurveyDelegate?)` | Set a delegate for survey lifecycle callbacks |

## 8 Question Types

Surveys support the following question types, configured in the Console:

| Type            | Display                | Response                 |
| --------------- | ---------------------- | ------------------------ |
| `nps`           | 0-10 numeric scale     | Integer 0-10             |
| `csat`          | 1-5 satisfaction scale | Integer 1-5              |
| `rating`        | Star rating            | Integer 1-5              |
| `emoji_scale`   | Emoji options          | Selected emoji value     |
| `yes_no`        | Binary choice          | Boolean                  |
| `single_choice` | Radio button options   | Selected option string   |
| `multi_choice`  | Checkbox options       | List of selected strings |
| `free_text`     | Text input field       | Free-form string         |

<Info>
  Question types and their content are defined entirely in the Console. The SDK renders them automatically based on the survey configuration.
</Info>

## AppDNASurveyDelegate

All 3 methods on this delegate fire from the native survey pipeline alongside the existing analytics events. Register your delegate via `AppDNA.surveys.setDelegate(...)`.

* `onSurveyPresented(surveyId)` -- fires when the survey view appears (alongside the `survey_shown` event).
* `onSurveyCompleted(surveyId, responses)` -- fires when the user submits the final question. `responses` is a `List<Map<String, dynamic>>` where each entry is an answer map of the shape `{'questionId': String, 'answer': dynamic}`, suitable for forwarding to your own analytics or CRM.
* `onSurveyDismissed(surveyId)` -- fires when the user closes the survey before completing it (X button, swipe-to-dismiss, outside tap).

Implement the delegate to capture survey responses and lifecycle events:

```dart theme={null}
class FeedbackHandler extends AppDNASurveyDelegate {
  @override
  void onSurveyPresented(String surveyId) {
    print("Survey shown: $surveyId");
  }

  @override
  void onSurveyCompleted(String surveyId, List<Map<String, dynamic>> responses) {
    // `responses` is a list of answer maps: {'questionId': String, 'answer': dynamic}.
    dynamic answerFor(String questionId) => responses
        .firstWhere((r) => r["questionId"] == questionId, orElse: () => {})["answer"];

    final score = answerFor("nps_question") as int?;
    if (score != null) {
      if (score >= 9) {
        showReferralPrompt();
      } else if (score <= 6) {
        showSupportLink();
      }
    }
    final feedback = answerFor("free_text_question") as String?;
    if (feedback != null) {
      print("User feedback: $feedback");
    }
  }

  @override
  void onSurveyDismissed(String surveyId) {
    print("Survey dismissed: $surveyId");
  }
}

AppDNA.surveys.setDelegate(FeedbackHandler());
```

### Response Shape

The `responses` list passed to `onSurveyCompleted` holds one answer map per answered question, each of the shape `{'questionId': String, 'answer': dynamic}`. The `answer` value type follows the question type:

| Question Type                               | `answer` Type  |
| ------------------------------------------- | -------------- |
| `nps`, `csat`, `rating`                     | `int`          |
| `yes_no`                                    | `bool`         |
| `single_choice`, `emoji_scale`, `free_text` | `String`       |
| `multi_choice`                              | `List<String>` |

## Rich Media

Survey questions support rich media content configured in the Console:

* **Header images** -- add images above survey questions
* **Icons in options** -- use icon references in choice options (Lucide, SF Symbols, Material, or emoji)
* **Thank-you animations** -- Lottie or confetti effects on survey completion
* **Haptic feedback** -- triggered on option selection and submission

See the [Rich Media](/sdks/flutter/rich-media) guide for details on supported formats.

## Auto-Tracked Events

| Event              | Trigger                             |
| ------------------ | ----------------------------------- |
| `survey_shown`     | A survey is displayed               |
| `survey_completed` | User submits a survey response      |
| `survey_dismissed` | Survey is closed without completing |

## Full Example

```dart theme={null}
import 'package:appdna_sdk/appdna_sdk.dart';

class FeedbackManager extends AppDNASurveyDelegate {
  FeedbackManager() {
    AppDNA.surveys.setDelegate(this);
  }

  /// Present NPS survey after a key moment
  Future<void> askForFeedback() async {
    await AppDNA.surveys.present("nps_q1_2026");
  }

  @override
  void onSurveyPresented(String surveyId) {
    // Survey is visible
  }

  @override
  void onSurveyCompleted(String surveyId, List<Map<String, dynamic>> responses) {
    // `responses` is a list of answer maps: {'questionId': String, 'answer': dynamic}.
    final nps = responses.firstWhere(
      (r) => r["questionId"] == "nps_question",
      orElse: () => {},
    );
    final score = nps["answer"] as int?;
    if (score == null) return;

    if (score >= 9) {
      showReferralPrompt();
    } else if (score <= 6) {
      showSupportLink();
    }
  }

  @override
  void onSurveyDismissed(String surveyId) {
    // User dismissed without responding
  }
}
```

<Note>
  Surveys are created in the Console under **Feedback > Surveys**. Trigger-based surveys appear automatically -- manual presentation with `present()` is for cases where you want precise control over timing.
</Note>
