Skip to main content

Flutter SDK

See how easy it is to integrate high-performance ads into your Flutter app using our lightweight SDK.

Requirements

Getting started

WebView prerequisites (flutter_inappwebview)

The Flutter SDK renders ads inside a WebView using flutter_inappwebview. To prevent WebView initialization errors, add this to your app entry point:
import 'package:flutter/widgets.dart';

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

  runApp(const MyApp());
}

1. Installation

To get started, you will need to set up a publisher account to get a publisherToken and code.
Add the package to your pubspec.yaml:
dependencies:
  kontext_flutter_sdk: ^<latest_version>
Install dependencies:
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/

2. Set up the Character object

Define the assistant’s character information:
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:
final regulatory = Regulatory(
  coppa: 0, 
  // ... other regulatory properties
);

4. Set up IFA (Identifier for Advertisers)

The SDK automatically reads and forwards the IFA with each ad request once the user has granted permission. To enable this, you need to complete a one-time setup on each platform.

iOS

Apple requires a usage description before the ATT (App Tracking Transparency) prompt can be shown. Add the following to your ios/Runner/Info.plist:
<key>NSUserTrackingUsageDescription</key>
<string>We use your advertising identifier to show you more relevant ads.</string>
Without this key your app will crash on iOS 14+. The string is displayed in the system permission dialog, so make sure it clearly explains why tracking is needed.
The SDK calls ATTrackingManager.requestTrackingAuthorization automatically. To avoid App Store rejection, ensure the prompt is triggered at a natural point in your app flow (e.g. after onboarding) rather than immediately on launch.
The ATT prompt requires the app to be in an active state to appear. Initializing the SDK too early (e.g. in main() before the first screen is rendered) may silently suppress the prompt. Initialize the SDK only after the app becomes active.

Android

On Android 13+ (API level 33+), declare the Ad ID permission in your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
No runtime prompt is required — this is an install-time permission.

5. Set up SKAdNetwork (iOS only)

SKAdNetwork (SKAN) is Apple’s privacy-preserving install attribution framework. To allow DSPs to measure ad conversions on iOS, you need to add their SKAdNetwork identifiers to your ios/Runner/Info.plist.
<key>SKAdNetworkItems</key>
<array>
  <dict>
    <key>SKAdNetworkIdentifier</key>
    <string>XXX.skadnetwork</string>
  </dict>
  <!-- Add all provided identifiers here -->
</array>
The full list of required SKAdNetwork identifiers will be provided during onboarding. Make sure to add all of them, as missing an identifier means conversions from that DSP will not be attributed.
If your app already has an existing SKAdNetworkItems array, do not replace it — append the new identifiers to the existing list to avoid breaking attribution for other ad networks you may be running.

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.
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:
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

publisherToken
String
required
Your unique publisher token.
messages
List<Message>
required
List of messages between the assistant and the user.
userId
String
required
Unique identifier that remains the same for the user’s lifetime (used for retargeting and rewarded ads).
userEmail
String
Email of the user.
conversationId
String
required
Unique ID of the conversation.
enabledPlacementCodes
List<String>
required
Placement codes enabled for the conversation. Example: ['inlineAd'].
character
Character
required
The character object used in a conversation.
regulatory
Regulatory
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.
variantId
String
Publisher-provided identifier for the user cohort (for A/B testing).
otherParams
Map<String, dynamic>
Used to pass publisher-specific information to Kontext.
isDisabled
bool
Flag indicating if the ads are disabled.
onEvent
void Function(AdEvent event)
Callback triggered when an event occurs. See AdEvent types for more details.
child
Widget
required
The widget subtree wrapped by AdsProvider.

InlineAd properties

code
String
required
The ad format code that identifies the ad to be displayed.
messageId
String
required
A unique identifier for the message associated with this ad.

AdEvent types

ad.clicked

The user has clicked the ad.

ad.viewed

The user has viewed the ad.

ad.filled

Ad is available.

ad.no-fill

Ad is not available.

ad.render-started

Triggered before the first token is received.

ad.render-completed

Triggered after the last token is received.

ad.error

Triggered when an error occurs.

reward.granted

Triggered when the user receives a reward.

video.started

Triggered when the video playback starts.

video.completed

Triggered when the video playback finishes.

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:
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.