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

# Flutter SDK

> Get started with our SDK built for iOS and Android apps using Flutter

<Info>
  We are preparing a new v4 release of the Flutter SDK. The documentation below covers the current shipping version — once v4 lands, this page will be updated.
</Info>

<Card title="Flutter SDK" icon="github" href="https://github.com/kontextso/sdk-flutter">
  See how easy it is to integrate high-performance ads into your Flutter app using our lightweight SDK.
</Card>

## Requirements

* **Flutter**: 3.24.0 or later
* **Dart**: 3.5.0 or later
* **Android**: `minSdkVersion >= 21`, `compileSdk >= 34`, [AGP](https://developer.android.com/build/releases/gradle-plugin) version `>= 7.3.0` (use [Android Studio - Android Gradle plugin Upgrade Assistant](https://developer.android.com/build/agp-upgrade-assistant) for help), support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app)
* **iOS**: `12.0+, --ios-language swift`, Xcode version `>= 15.0`

## Getting started

### WebView prerequisites (`flutter_inappwebview`)

The Flutter SDK renders ads inside a WebView using [flutter\_inappwebview](https://pub.dev/packages/flutter_inappwebview).
To prevent WebView initialization errors, add this to your app entry point:

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

void main() {
  // Must be first so plugins are ready.
  WidgetsFlutterBinding.ensureInitialized();

  runApp(const MyApp());
}
```

### 1. Installation

<Note>
  To get started, you will need to set up a [publisher account](/publishers#getting-started-is-easy) to get a `publisherToken` and `code`.
</Note>

Add the package to your `pubspec.yaml`:

```yaml theme={null}
dependencies:
  kontext_flutter_sdk: ^<latest_version>
```

Install dependencies:

```bash theme={null}
flutter pub get
```

Ensure your project meets the Android min/compile SDK and iOS/Xcode requirements listed above.
If you run into issues, verify that your project meets the plugin’s platform requirements: [https://inappwebview.dev/docs/intro/](https://inappwebview.dev/docs/intro/)

### 2. Set up the `Character` object

Define the assistant’s character information:

```dart theme={null}
final character = Character(
  id: 'id-123',
  name: 'Ava',
  avatarUrl: 'https://example.com/avatar.png',
  greeting: 'Hi there! How can I help you today?'
);
```

### 3. Set up the `Regulatory` object

Next, define the user’s regulatory context:

```dart theme={null}
final regulatory = Regulatory(
  coppa: 0, 
  // ... other regulatory properties
);

```

### 4. Set up IFA (Identifier for Advertisers)

* **iOS:** add `NSUserTrackingUsageDescription` to `ios/Runner/Info.plist`. The SDK auto-prompts for ATT and reads IDFA from there.
* **Android (13+):** declare `com.google.android.gms.permission.AD_ID` in `android/app/src/main/AndroidManifest.xml` — this is an install-time permission with no runtime prompt.

See [IFA & ATT](/guides/ifa) for the full setup — required keys, prompt-timing gotchas, and how to manage the prompt yourself.

### 5. Set up SKAdNetwork (iOS only)

Add the SKAdNetwork identifiers we provide during onboarding to `ios/Runner/Info.plist`. The SDK reads them and forwards them on every `/init` so DSPs can measure conversions.

See [SKAdNetwork](/guides/skadnetwork) for the full guide.

### 6. Set up the `AdsProvider`

Wrap your app (or the part of it that contains ad placements) with the `AdsProvider`.
The `AdsProvider` is responsible for fetching and managing ads, and it requires access to the current chat `messages`.

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

// Messages between user and assistant
final messages = [
  Message(
    id: 'msg-001',
    role: MessageRole.assistant,
    content: 'Hello! How can I help you today?',
    createdAt: DateTime.parse('2025-08-31T10:00:00Z'),
  ),
  Message(
    id: 'msg-002',
    role: MessageRole.user,
    content: 'Show me today's workout plan.',
    createdAt: DateTime.parse('2025-08-31T10:00:05Z'),
  ),
  Message(
    id: 'msg-003',
    role: MessageRole.assistant,
    content: 'Here's a 30-minute routine to start with.',
    createdAt: DateTime.parse('2025-08-31T10:00:10Z'),
  ),
];

Widget build(BuildContext context) {
  return AdsProvider(
    publisherToken: '<your-publisher-token>',
    userId: 'user-1234',                 
    conversationId: 'conv-5678', 
    enabledPlacementCodes: ['inlineAd'],
    messages: messages,
    character: character,  // From section 2
    regulatory: regulatory, // From section 3
    otherParams: {
      'theme': 'dark',
    },
    child: YourChatWidget(),
  );
}
```

### 7. Display your first ad

An ad slot is a designated area in your UI where an ad can be rendered.
In most cases, it appears below a chat message.
During onboarding, you’ll receive a unique `code` for each ad slot you plan to use.

Example using the `InlineAd` format:

```dart theme={null}
ListView.builder(
  itemCount: messages.length,
  itemBuilder: (context, index) {
    final message = messages[index];
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(message.content),
        InlineAd(
          code: '<your-code>',
          messageId: message.id,
        ),
      ],
    );
  },
)
```

> 💡 **Note:** `InlineAd` does not always display an ad. Whether an ad is shown depends on the context of the ongoing conversation.
> If no ad is available, `InlineAd` automatically returns a `const SizedBox.shrink()`, so it won’t take up any extra space in your layout.

## API documentation

### `AdsProvider` properties

<ParamField path="publisherToken" type="String" required>
  Your unique publisher token.
</ParamField>

<ParamField path="messages" type="List<Message>" required>
  List of messages between the assistant and the user.

  <Expandable title="Properties">
    <ParamField path="id" type="String" required>
      Unique ID of the message.
    </ParamField>

    <ParamField path="role" type="enum" required>
      Role of the message (`MessageRole.user` or `MessageRole.assistant`).
    </ParamField>

    <ParamField path="content" type="String" required>
      Message text.
    </ParamField>

    <ParamField path="createdAt" type="DateTime" required>
      Timestamp when the message was created.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="userId" type="String" required>
  Unique identifier that remains the same for the user's lifetime (used for retargeting and rewarded ads).
</ParamField>

<ParamField path="userEmail" type="String" optional>
  Email of the user.
</ParamField>

<ParamField path="conversationId" type="String" required>
  Unique ID of the conversation.
</ParamField>

<ParamField path="enabledPlacementCodes" type="List<String>" required>
  Placement codes enabled for the conversation. Example: `['inlineAd']`.
</ParamField>

<ParamField path="character" type="Character" required>
  The character object used in a conversation.

  <Expandable title="Properties">
    <ParamField path="id" type="String" required>
      Unique identifier for the character.
    </ParamField>

    <ParamField path="name" type="String" required>
      Name of the character.
    </ParamField>

    <ParamField path="avatarUrl" type="String" required>
      URL of the character's avatar image.
    </ParamField>

    <ParamField path="isNsfw" type="bool" optional>
      Whether the character is NSFW (Not Safe For Work).
    </ParamField>

    <ParamField path="greeting" type="String" optional>
      A greeting message from the character.
    </ParamField>

    <ParamField path="persona" type="String" optional>
      A description of the character's persona.
    </ParamField>

    <ParamField path="tags" type="List<String>" optional>
      Tags associated with the character.
    </ParamField>

    <ParamField path="additionalProperties" type="Map<String, dynamic>" optional>
      Additional properties that can be added to the character.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="regulatory" type="Regulatory" optional>
  Regulatory compliance information.

  **TCF (Transparency and Consent Framework)** signals — `gdpr` and `gdprConsent` — are handled automatically by the SDK if you have a TCF-compliant CMP (Consent Management Platform) integrated in your app. You do not need to set these manually.

  <Expandable title="Properties">
    <ParamField path="gdpr" type="int" optional>
      Flag that indicates whether or not the request is subject to GDPR regulations (0 = No, 1 = Yes, null = Unknown).
    </ParamField>

    <ParamField path="gdprConsent" type="String" optional>
      The IAB Transparency and Consent Framework (TCF) consent string.
    </ParamField>

    <ParamField path="coppa" type="int" optional>
      Flag whether the request is subject to COPPA (0 = No, 1 = Yes, null = Unknown).
    </ParamField>

    <ParamField path="gpp" type="String" optional>
      Global Privacy Platform (GPP) consent string.
    </ParamField>

    <ParamField path="gppSid" type="List<int>" optional>
      List of the section(s) of the GPP string which should be applied for this transaction.
    </ParamField>

    <ParamField path="usPrivacy" type="String" optional>
      Communicates signals regarding consumer privacy under US privacy regulation under CCPA and LSPA.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField path="variantId" type="String" optional>
  Publisher-provided identifier for the user cohort (for A/B testing).
</ParamField>

<ParamField path="otherParams" type="Map<String, dynamic>" optional>
  Used to pass publisher-specific information to Kontext.
</ParamField>

<ParamField path="isDisabled" type="bool" optional>
  Flag indicating if the ads are disabled.
</ParamField>

<ParamField path="onEvent" type="void Function(AdEvent event)" optional>
  Callback triggered when an event occurs. See [AdEvent types](#adevent-types) for more details.
</ParamField>

<ParamField path="child" type="Widget" required>
  The widget subtree wrapped by `AdsProvider`.
</ParamField>

### `InlineAd` properties

<ParamField path="code" type="String" required>
  The ad format code that identifies the ad to be displayed.
</ParamField>

<ParamField path="messageId" type="String" required>
  A unique identifier for the message associated with this ad.
</ParamField>

### AdEvent types

#### `ad.clicked`

The user has clicked the ad.

<Expandable title="Payload">
  <ParamField path="id" type="String">
    Ad ID.
  </ParamField>

  <ParamField path="content" type="String">
    Generated ad content.
  </ParamField>

  <ParamField path="messageId" type="String">
    Message ID.
  </ParamField>

  <ParamField path="url" type="String">
    Click URL.
  </ParamField>

  <ParamField path="format" type="String">
    Ad format.
  </ParamField>
</Expandable>

#### `ad.viewed`

The user has viewed the ad.

<Expandable title="Payload">
  <ParamField path="id" type="String">
    Ad ID.
  </ParamField>

  <ParamField path="content" type="String">
    Generated ad content.
  </ParamField>

  <ParamField path="messageId" type="String">
    Message ID.
  </ParamField>

  <ParamField path="format" type="String">
    Ad format.
  </ParamField>

  <ParamField path="revenue" type="Double">
    The revenue of the ad (eCPM in USD).
  </ParamField>
</Expandable>

#### `ad.filled`

Ad is available.

<Expandable title="Payload">
  <ParamField path="revenue" type="Double">
    The revenue of the ad (eCPM in USD).
  </ParamField>
</Expandable>

#### `ad.no-fill`

Ad is not available.

<Expandable title="Payload">
  <ParamField path="skipCode" type="String">
    The code indicating the reason why the ad was skipped.
  </ParamField>
</Expandable>

#### `ad.render-started`

Triggered before the first token is received.

<Expandable title="Payload">
  <ParamField path="id" type="String" required>
    Ad ID.
  </ParamField>
</Expandable>

#### `ad.render-completed`

Triggered after the last token is received.

<Expandable title="Payload">
  <ParamField path="id" type="String" required>
    Ad ID.
  </ParamField>
</Expandable>

#### `ad.error`

Triggered when an error occurs.

<Expandable title="Payload">
  <ParamField path="message" type="String" required>
    Error message.
  </ParamField>

  <ParamField path="errCode" type="String" required>
    Error code.
  </ParamField>
</Expandable>

#### `reward.granted`

Triggered when the user receives a reward.

<Expandable title="Payload">
  <ParamField path="id" type="String" required>
    Ad ID.
  </ParamField>
</Expandable>

#### `video.started`

Triggered when the video playback starts.

<Expandable title="Payload">
  <ParamField path="id" type="String" required>
    Ad ID.
  </ParamField>
</Expandable>

#### `video.completed`

Triggered when the video playback finishes.

<Expandable title="Payload">
  <ParamField path="id" type="String" required>
    Ad ID.
  </ParamField>
</Expandable>

## Guides

### Handling no-fill events

You can detect when no ad is available by using the `onEvent` callback and listening for the `ad.no-fill` event.

## Troubleshooting

### Missing plugin warnings

If you see warnings like `MissingPluginException` or errors about a plugin not being registered, try the following:

```bash theme={null}
flutter clean
flutter pub get
```

This clears cached build artifacts and ensures plugins are re-registered.
If the problem persists, try rebuilding your app `flutter run` or restarting your IDE.

## Links

* [Flutter SDK GitHub](https://github.com/kontextso/sdk-flutter)
* [Pub](https://pub.dev/packages/kontext_flutter_sdk)
* [Changelog](https://github.com/kontextso/sdk-flutter/blob/main/CHANGELOG.md)
