> ## 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 In-App Messages

> Trigger-based banners, modals, and fullscreen messages

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

In-app messages are configured in the AppDNA Console and displayed automatically by the SDK when trigger conditions are met. **No code is required** to show messages -- the SDK evaluates triggers on every tracked event and presents the message if conditions match.

## How It Works

1. You create an in-app message in the Console with content, layout, and trigger rules.
2. The message definition is synced to the SDK via the config bundle.
3. On every `track()` call (including auto-tracked events), the SDK evaluates all active message triggers.
4. If conditions match, the message is presented automatically.

<Info>
  In-app messages are fully server-driven. You can change the message content, trigger rules, and audience without an app update.
</Info>

## Message Types

| Type         | Description                                  |
| ------------ | -------------------------------------------- |
| `banner`     | Small bar at the top or bottom of the screen |
| `modal`      | Centered overlay with a dimmed background    |
| `fullscreen` | Full-screen takeover                         |
| `tooltip`    | Small popup anchored to a UI element         |

## Triggers

Messages trigger based on events and optional conditions, configured in the Console:

* **Event match** -- e.g., trigger on `workout_completed`
* **Property conditions** -- e.g., `duration >= 30`
* **Frequency** -- `once`, `once_per_session`, or `every_time`
* **Delay** -- wait N seconds after the trigger event before showing

## Module Access

```dart theme={null}
final messages = AppDNA.inAppMessages;
```

### Module Methods

| Method            | Signature                                                | Description                          |
| ----------------- | -------------------------------------------------------- | ------------------------------------ |
| `suppressDisplay` | `Future<void> suppressDisplay(bool suppress)`            | Suppress or resume in-app messages   |
| `setDelegate`     | `void setDelegate(AppDNAInAppMessageDelegate? delegate)` | Set a delegate for message callbacks |

## Suppressing Messages

Suppress messages during critical flows like checkout or onboarding to avoid interrupting the user:

```dart theme={null}
// Suppress during purchase flow
await AppDNA.inAppMessages.suppressDisplay(true);
startPurchaseFlow();

// Resume after purchase completes
Future<void> onPurchaseComplete() async {
  await AppDNA.inAppMessages.suppressDisplay(false);
}
```

## AppDNAInAppMessageDelegate

All 4 methods on this delegate are dispatched from the native `MessageManager` for every in-app message lifecycle event. Register your delegate via `AppDNA.inAppMessages.setDelegate(...)`.

**`shouldShowMessage` is a true veto.** The SDK calls it BEFORE constructing the view or tracking the `in_app_message_shown` analytics event. If you return `false`, the message is suppressed entirely — no view, no analytics. The abstract class's default implementation returns `true`, so hosts that don't override this method continue to see all messages.

The 4 methods semantics:

* `onMessageShown(messageId, trigger)` — fired after the SDK constructed the view AND emitted the `in_app_message_shown` analytics event. Use for app-side telemetry or routing logic.
* `onMessageAction(messageId, action, data)` — fired when the user taps a CTA, deep-link, or custom action button. The `data` map carries action-specific payload (URL for deep links, custom keys you set in the Console).
* `onMessageDismissed(messageId)` — fired exactly once when the message is dismissed by the user (close button, swipe, backdrop tap) or programmatically.
* `shouldShowMessage(messageId)` — veto checked before display + analytics. Default returns `true`.

Implement the delegate to respond to message lifecycle events:

```dart theme={null}
abstract class AppDNAInAppMessageDelegate {
  void onMessageShown(String messageId, String trigger);

  void onMessageAction(
    String messageId,
    String action,
    Map<String, dynamic>? data,
  );

  void onMessageDismissed(String messageId);

  /// Veto. Return false to suppress display.
  bool shouldShowMessage(String messageId) => true;
}
```

### Example Implementation

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

class MessageHandler extends AppDNAInAppMessageDelegate {
  @override
  void onMessageShown(String messageId, String trigger) {
    print("Message shown: $messageId (trigger: $trigger)");
  }

  @override
  void onMessageAction(
    String messageId,
    String action,
    Map<String, dynamic>? data,
  ) {
    final url = data?['url'] as String?;
    if (url != null) {
      // Handle deep link or URL action
      navigate(url);
    }
  }

  @override
  void onMessageDismissed(String messageId) {
    print("Message dismissed: $messageId");
  }

  @override
  bool shouldShowMessage(String messageId) {
    // Return false to prevent the message from being shown.
    return true;
  }

  void navigate(String url) { /* ... */ }
}

AppDNA.inAppMessages.setDelegate(MessageHandler());
```

## Rich Media

In-app messages support rich media content configured in the Console:

* **Lottie animations** -- animated hero images or backgrounds
* **Video** -- inline video with optional autoplay and looping
* **Icon buttons** -- CTA buttons with icon references (Lucide, SF Symbols on iOS, Material on Android, or emoji)
* **Haptic feedback** -- triggered on message display or button taps
* **Particle effects** -- confetti, sparkles, or other effects on message actions
* **Blur backdrop** -- glassmorphism-style blurred background for modals

Rich media uses native iOS rendering (SwiftUI + CoreAnimation) and native Android rendering (Jetpack Compose + Rive) for full 60fps animations at native performance.

## Auto-Tracked Events

| Event                      | Trigger                                                                       |
| -------------------------- | ----------------------------------------------------------------------------- |
| `in_app_message_shown`     | An in-app message is displayed                                                |
| `in_app_message_clicked`   | User taps an action in the message                                            |
| `in_app_message_dismissed` | Message is closed                                                             |
| `in_app_message_received`  | (Android pending-message channel) A message is received before display gating |

## Full Example

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

class AppCoordinator extends AppDNAInAppMessageDelegate {
  AppCoordinator() {
    AppDNA.inAppMessages.setDelegate(this);
  }

  Future<void> startOnboarding() async {
    // Suppress messages during onboarding
    await AppDNA.inAppMessages.suppressDisplay(true);
    presentOnboardingFlow();
  }

  Future<void> onboardingDidFinish() async {
    // Resume messages after onboarding
    await AppDNA.inAppMessages.suppressDisplay(false);
  }

  // AppDNAInAppMessageDelegate

  @override
  void onMessageShown(String messageId, String trigger) {
    // Optionally track in your own analytics
  }

  @override
  void onMessageAction(
    String messageId,
    String action,
    Map<String, dynamic>? data,
  ) {
    final url = data?['url'] as String?;
    if (url != null) {
      navigate(url);
    } else if (action == 'dismiss') {
      // no-op
    }
  }

  @override
  void onMessageDismissed(String messageId) {
    // Message closed
  }

  @override
  bool shouldShowMessage(String messageId) => true;

  void navigate(String url) { /* ... */ }
  void presentOnboardingFlow() { /* ... */ }
}
```

<Note>
  In-app messages are created in the Console under **Engagement > In-App Messages**. Design the message, set trigger rules, and publish. The SDK handles everything else.
</Note>
