<!-- AI agents: this is the Markdown version of a Reception documentation page. Index: /llms.txt -->

# Flutter

Add Reception's native iOS support chat to a Flutter app with the `reception_flutter` package. The chat runs entirely in the official Reception iOS SDK, which the package bundles; the Dart API mirrors the [React Native](/docs/react-native) and [native iOS](/docs/native-ios) ones. It needs Flutter 3.44+, Dart 3.12+, iOS 17+ and Xcode 26. Android and web calls complete immediately, log once and return default state.

Most teams let a coding agent do the setup: copy the prompt from **Settings → your app → SDK setup** in the dashboard; it detects Flutter from `pubspec.yaml`. The rest of this page is what the agent follows and what you need when you change things by hand.

## 1. Install {#install}

```sh
flutter pub add reception_flutter
```

Swift Package Manager and CocoaPods both work without Podfile edits. Set the iOS deployment target to 17.0 or higher at the project level (Xcode: Runner project → Info → iOS Deployment Target, or the three `IPHONEOS_DEPLOYMENT_TARGET` entries in `ios/Runner.xcodeproj/project.pbxproj`), and raise any Runner target-level entries below 17.0. If `ios/Podfile` exists and pins an older platform, set `platform :ios, '17.0'` there too; a `post_install` hook that forces an older target on all pods must use 17.0 or skip `reception_flutter`. The chat follows the languages declared under `CFBundleLocalizations` in `ios/Runner/Info.plist`.

Do not also add the native `reception-ios` package: the plugin already bundles the SDK, and two copies would share the same storage.

## 2. Configure {#configure}

Configure once in `main()`, before showing Support, and after `Firebase.initializeApp()` and any other notification plugin's setup. Your public App ID is in the dashboard under **Settings → your app → General**.

```dart
import 'package:flutter/widgets.dart';
import 'package:reception_flutter/reception_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Reception.configure(appId: 'app_YOUR_APP_ID');
  runApp(const MyApp());
}
```

`Reception.isConfigured` tells whether the native side accepted the configuration. Pass `handlesNotifications: false` only if your app routes Reception's own notifications itself; see [Push](#push).

## 3. Open chat {#open-chat}

Call this from your app's Support button, shown only on iOS (`!kIsWeb && defaultTargetPlatform == TargetPlatform.iOS`); keep your existing contact path on other platforms:

```dart
await Reception.openChat();
```

`Reception.closeChat()` dismisses the presented sheet. `Reception.isChatOpen` is a `ValueListenable<bool>` of whether the chat is visible.

Report app events for [automations](/docs/automations) with `await Reception.track('paywall_declined', opensChat: true)`.

## Appearance and language {#appearance}

Assign a complete `ReceptionAppearance`; it applies immediately. Colors take a hex string or an adaptive light/dark pair, read from your app's theme so both modes follow the app.

```dart
Reception.appearance = const ReceptionAppearance(
  accentColor: ReceptionColor.adaptive(light: '#635BFF', dark: '#A59FFF'),
  onAccentColor: '#FFFFFF',
  chatBackground: ReceptionColor.adaptive(light: '#FFFFFF', dark: '#151225'),
  incomingBackground: ReceptionColor.adaptive(light: '#F2F2F7', dark: '#2C2C2E'),
  incomingForeground: ReceptionColor.adaptive(light: '#000000', dark: '#FFFFFF'),
  title: 'Support',
  showsCloseButton: true,
);
```

Other options: `welcomeTitle`, `welcomeText`, `welcomeIcon` (a symbol, an iOS asset image, or none) with `welcomeIconColor`, `welcomeIconSize`, `welcomeIconCornerRadius` and `welcomeIconOffset`; `preferredColorScheme` for an app that is always light or dark; `closeIcon` or `closeImage`; `fontDesign` or `fontFamily` (a font registered with iOS, not a `pubspec.yaml` font); `fadesOlderMessages`; and `hidesTitle`, `hidesWelcomeTitle`, `hidesWelcomeText` and `hidesPushStatus` (the "You'll get a notification when we reply." line). The dashboard's [remote appearance](/docs/remote-appearance) overrides these without an app update unless you set `Reception.usesRemoteAppearance = false`.

The chat follows the app's language by itself. Only if your app has its own in-app language setting, set `Reception.languageOverride = 'de'` from it and update it when it changes; `null` follows the device. Texts are built in for 48 languages; see [Text & localization](/docs/language).

## Entry and unread badges {#unread}

`Reception.unreadCount` is a `ValueListenable<int>`. Show it on the Support entry and on every menu row on the way, and hide the badge at zero:

```dart
ValueListenableBuilder<int>(
  valueListenable: Reception.unreadCount,
  builder: (context, unread, _) => Badge.count(
    count: unread,
    isLabelVisible: unread > 0,
    child: const Icon(Icons.chat_bubble_outline),
  ),
)
```

The count updates while the app runs and after launch; see [Unread messages & badges](/docs/unread-messages). `Reception.updatesAppBadge = false` keeps the app icon badge under your control.

## Accounts and user details {#identity}

Identify the person so the team knows who writes:

```dart
await Reception.identify(ReceptionIdentity.user(userId: user.id, name: user.name, email: user.email));
await Reception.setMetadata({'plan': 'pro', 'orders': '12'});
```

- `ReceptionIdentity.user` labels the device with a real signed-in account ID and optional name and email; use it with no user ID for a name the person entered without an account.
- `ReceptionIdentity.token` carries a signed token from your server for [identity verification](/docs/accounts). The chat emits `ReceptionIdentityRejected` when the server refuses it.
- `setMetadata` replaces the whole dictionary (string values, up to 20 fields); names and emails belong in `identify`.
- Call `Reception.logout()` wherever a signed-in session ends, before the next person's `identify`, and `await Reception.deleteData()` on account deletion. Both clear the user details and the push token; send them again with the next sign-in. `deleteData` throws a `ReceptionError` with code and HTTP status on failure.

## Push {#push}

Add the Push Notifications capability to the Runner target and your Apple push credentials in the dashboard under **Settings → your app → Push Notifications**. With the default `handlesNotifications: true`, the plugin then:

- registers the APNs device token with Reception,
- shows Reception banners while the chat is closed and stays quiet while it is open,
- opens the chat when a Reception notification is tapped, including cold starts,
- keeps forwarding notifications from Firebase, OneSignal and other services to their own handlers.

It never requests permission: after your own permission request, call `await Reception.registerForPushIfAuthorized()`. If you prefer, turn on **Ask for push notifications** in the dashboard: the chat then asks with a small card right after the person's first message, with no code in your app; see [Push notifications](/docs/push-notifications#chat-prompt).

Apps that get the APNs token elsewhere, for example from Firebase's `getAPNSToken()`, pass it on with `Reception.setPushToken(hexToken)`; FCM tokens are rejected. Apps that route notifications themselves (`handlesNotifications: false`) call `await Reception.handlePushNotification(payload, openChat: tapped)` for every notification; it returns `true` for Reception's. Payload shape, APNs setup and verification are on the [push notifications page](/docs/push-notifications).

## Paywalls {#paywalls}

Register the paywalls your app can show, so the team, automations and the AI can send [offer cards](/docs/paywalls) that open them:

```dart
Reception.paywalls = [ReceptionPaywall(id: 'plans', title: 'Plans')];
Reception.onPaywall = (id) => showPaywall(id); // the chat stays open behind it
Reception.paywallAvailability = (id) async => !await isSubscribed(); // optional
```

`Reception.availablePaywalls` lists the registered paywalls that are available right now. Call `Reception.refreshPaywalls(invalidate: true)` after a purchase or subscription change. Pending offers become unavailable on failures, timeouts and hot restarts. The [paywalls page](/docs/paywalls) has drop-in files for RevenueCat, Superwall and Adapty that fill `paywalls` and report the person's plan as user details.

## Screens for link cards {#screens}

[Link cards](/docs/links) open websites, other apps, the App Store, Mail and the phone without code. To let them open screens of your app too, list the screens once at startup; the dashboard then offers them by name and sends a screen link only to app versions that have it:

```dart
Reception.screens = [
  ReceptionScreen('settings', onOpen: () => router.push('/settings')),
  ReceptionScreen('paywall', onOpen: showPaywall),
  ReceptionScreen.withValue('order', value: 'Order number', onOpenWithValue: (number) => router.push('/orders/$number')),
];
```

`onOpen` runs after the chat closes. IDs use letters, digits, `_`, `-` or `.`, at most 64 characters, and stay stable: a renamed ID is a new screen. Set the list again at startup after a hot restart; until then the app reports no screens. The dashboard's **Add a link → Screen in your app** has a prompt for a coding agent that finds your screens and writes this list.

## Embedded chat {#embedded}

`ReceptionChatView()` embeds the chat in your own screen instead of the sheet; give it a defined size, and your screen owns dismissal. The welcome texts, appearance and unread count apply the same way. `Reception.closeChat()` does not close an embedded view.

## Events and diagnostics {#events}

`Reception.onEvent` receives `ReceptionChatOpened`, `ReceptionMessageSent`, `ReceptionImageSent`, `ReceptionReviewOpened`, `ReceptionPaywallOpened`, `ReceptionLinkOpened`, `ReceptionDataDeleted` and `ReceptionIdentityRejected(error)`. Assignment replaces the previous handler; a hot restart clears it, so assign it in `main()`. See [Events & callbacks](/docs/events).

`Reception.logLevel = ReceptionLogLevel.debug` prints the SDK's log lines (subsystem `com.reception.sdk` in Console) while you set things up. `Reception.version` is the bundled iOS SDK version.

## Build and simulator checks {#simulator-checks}

Build and run on an iOS simulator with `flutter run`, or build once with `flutter build ios --simulator --debug` and install `build/ios/iphonesimulator/Runner.app` with `xcrun simctl install`. Debug simulator builds support the SDK's launch arguments: `-ReceptionSetupCheck <app id>` sends the first message and logs the result, and `-ReceptionPreview welcome` or `conversation` opens the chat with example data; the [setup page](/docs/get-started) explains them. Let `main()` finish configuring Reception before reading the result.

For `package:reception_flutter/testing.dart`, `configureReceptionForTesting(appId:, serviceUrl:)` points a Debug simulator build at a local Reception backend; it is a no-op in release builds.

## Updating {#updating}

Run `flutter pub upgrade reception_flutter`, then `pod install` in `ios/` if the project has a Podfile, and rebuild. The [changelog](/changelog) lists what each release changes; the dashboard shows an update card when a newer version is available.
