Flutter SDK
Add Kerabie live chat to your iOS or Android app built with Flutter.
The kerabie_helpdesk_flutter package adds live chat to a Flutter app, with photos and files, a help centre, tickets, a store and proactive campaigns. Messages arrive in your Kerabie inbox.
Install
flutter pub add kerabie_helpdesk_flutterIt needs Flutter 3.27 or later and Dart 3.6 or later. Get your widget key from Settings, Widget in the dashboard.
On iOS, add the reason for the photo library to ios/Runner/Info.plist, so customers can attach photos:
<key>NSPhotoLibraryUsageDescription</key>
<string>Attach a photo to your message to support.</string>Quick start
Put KerabieProvider above your MaterialApp, and add the launcher over your screens.
import 'package:flutter/material.dart';
import 'package:kerabie_helpdesk_flutter/kerabie_helpdesk_flutter.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(
KerabieProvider(
navigatorKey: navigatorKey,
config: const KerabieConfig(
widgetKey: 'wk_yourorg_123',
appName: 'My App',
appVersion: '1.0.0',
user: KerabieUser(name: 'Jane', email: 'jane@example.com'),
),
child: MaterialApp(
navigatorKey: navigatorKey,
navigatorObservers: [KerabieNavigatorObserver()],
home: const Scaffold(
body: Stack(children: [HomeScreen(), KerabieLauncher()]),
),
),
),
);
}KerabieLauncher shows the floating button or the search bar, whichever the dashboard picked. To open the chat from your own button, use KerabieChat.show(context).
Configuration
| Field | Required | Description |
|---|---|---|
widgetKey | Yes | Your widget key from Settings, Widget |
appName, appVersion | Yes | Your app's name and version, sent when the chat connects |
appBuild | No | Build number, sent with them |
user | No | KerabieUser with id, name, email, phone, role and customAttributes |
theme | No | KerabieTheme overrides: primaryColor, secondaryColor, gradient, cornerRadius, mode (light, dark or auto), backgroundColor, textColor, agentBubbleColor, userBubbleColor, bubbleColor, bubbleIcon, bubbleSize, bubbleIconSize, fontFamily, chatbotAvatarUrl |
chatSettings | No | KerabieChatSettings overrides for the dashboard: name, welcomeMessage, offlineMessage, showBranding, typingIndicator, enableFileAttachments, chatbotEnabled, layout, showMenu, launcherType, searchbarPlaceholder, customerEnrichmentEnabled, customerEnrichmentForm |
openMode | No | modal (a full-screen page, the default), sheet (a bottom sheet) or push (your own navigator opens it) |
position | No | Where the launcher sits: bottomRight (default), bottomLeft, topRight, topLeft |
defaultOpen | No | Open the chat as soon as the app has built |
hideFloatingButton | No | Make KerabieLauncher draw nothing; open the chat from your own button |
deepLinkScheme | No | Your app's URL scheme, so only myapp://conversation/123 links open the chat |
Colours, the widget name, the welcome message, the offline message and branding come from Settings, Widget in the dashboard. Anything you set in theme or chatSettings wins over the dashboard. Light or dark comes from the dashboard's Theme setting: auto follows the device, and the chat is light when nothing is set. Text on a coloured background is dark or white depending on the colour's luminance, so it stays readable on any brand colour. The welcome message can use {{name}}, which becomes the signed-in user's first name.
KerabieProvider also takes onOpenChat, onCloseChat, onNavigate and onOpenKnowledgeBase.
Open modes
With openMode: KerabieOpenMode.push the SDK does not touch your navigator. It calls onNavigate and your app shows a KerabieChatScreen in its own route:
KerabieProvider(
config: const KerabieConfig(widgetKey: '...', appName: '...', appVersion: '...', openMode: KerabieOpenMode.push),
onNavigate: (route) => router.push(route.type == KerabieRouteType.conversation ? '/chat/${route.conversationId}' : '/chat'),
child: ...,
)
// In your /chat route:
KerabieChatScreen(onClose: () => router.pop())Without onNavigate the chat opens as a full-screen page.
Reading the chat state
final kerabie = KerabieProvider.of(context); // rebuilds when the chat changes
Badge(label: Text('${kerabie.unreadCount}'), child: const Icon(Icons.support_agent));| Member | Description |
|---|---|
messages | The conversation, oldest first |
unreadCount | Replies that arrived while the chat was closed |
isConnected | Whether the live connection is up |
isTyping, isAiTyping | An agent, or the AI assistant, is writing |
agentsOnline | How many agents are online, once the server has said |
settings, chat | What the dashboard saved, and the chat settings in force |
sendMessage(text), retry(message) | Sends a message, and sends a failed one again |
showChat(), dismissChat(), toggle() | Open and close the chat |
setUser(user), clearUser(), updateAttribute(key, value) | Who the customer is, see below |
setAppVersion(version) | The app's version changed; it is sent with the next connection |
openWithContext(context) | Opens the chat with context, see below |
Who the customer is
Call setUser after login so agents see who they are talking to, updateAttribute to add a detail, and clearUser after logout. clearUser empties the chat on screen and the next visitor starts a fresh anonymous session.
final kerabie = KerabieProvider.of(context, listen: false);
await kerabie.setUser(const KerabieUser(id: 'u_123', name: 'Ada Obi', email: 'ada@example.com'));
await kerabie.updateAttribute('plan', 'pro');
await kerabie.clearUser();Opening the chat with context
Tell the agent what the customer was looking at, so they do not have to explain it:
kerabie.openWithContext(const KerabieChatContext(
type: 'order', // order, product, transaction, ride, errand, booking or custom
id: 'ORD-2024-001',
title: 'Order #2024-001',
data: {'status': 'shipped', 'total': '45.50'},
));Pass openChat: false to only record it and open the chat later.
Screens
KerabieNavigatorObserver tells Kerabie which screen the customer is on, from your routes' names. Campaigns can be limited to certain screens ("On pages" in the dashboard). Dialogs, sheets and routes without a name are ignored. With a router that has no route names, such as go_router, report screens yourself with kerabie.trackScreen('/checkout').
Help centre, store and tickets
Ready-made screens you can push anywhere in your app. Each follows your theme and the dashboard settings.
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const KerabieKnowledgeBaseScreen()));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const KerabieStoreScreen()));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => const KerabieTicketsScreen()));kerabie.settings.ticketsEnabled says whether tickets are on in the dashboard, so you can show an entry point only when they are. For your own design use the data classes behind the screens: KerabieKnowledgeBase, KerabieStore and KerabieTickets.
Campaigns
Proactive messages you create in the dashboard (Campaigns) also appear in your app, above the launcher, with their button. They load once per app launch and need the same navigatorKey on KerabieProvider and MaterialApp.
| Dashboard setting | In the app |
|---|---|
| Trigger Page load | Shown about 1.5 seconds after a screen appears |
| Trigger Time on page | Shown after N seconds in the app |
| Trigger Scroll depth, Exit intent | Website only; not shown in apps |
| On pages | Matched against the current screen. Without a screen, only campaigns for every screen show |
| Audience | New and returning mean the first and a later launch on this device; "hasn't chatted" checks this device's conversations; country is decided by the server |
| Frequency | Remembered on the device: once per visitor, once per day, or once per app launch |
| Button Open chat / Open a link | Opens the chat / the link in the browser |
| Button Open Knowledge Base | Calls your onOpenKnowledgeBase; opens the chat if you do not pass it |
Results (shown, opened, clicked, replied) are counted in the dashboard like they are for the website widget. Nothing pops up while the chat is open.
Calls
Customers can call your team from the app: voice, and video if you turn it on. Calls are a separate, optional package, so apps that only want chat do not carry the call code.
flutter pub add kerabie_helpdesk_flutter_callsTurn calls on in the dashboard under Settings, Calls. Your plan decides how many call minutes you get each month, and calls the AI voice agent answers are counted separately.
The app needs permission to use the microphone, and the camera for video. On iOS add NSMicrophoneUsageDescription and NSCameraUsageDescription to Info.plist. On Android add the CAMERA, RECORD_AUDIO, ACCESS_NETWORK_STATE, CHANGE_NETWORK_STATE and MODIFY_AUDIO_SETTINGS permissions to the manifest, plus BLUETOOTH and BLUETOOTH_ADMIN limited to maxSdkVersion="30". The package README has the exact lines.
import 'package:kerabie_helpdesk_flutter_calls/kerabie_helpdesk_flutter_calls.dart';
// Anywhere under KerabieProvider. It shows nothing when calls are off or the plan has no minutes left.
const KerabieCallButton(label: 'Call us'),
const KerabieCallButton(label: 'Video call', video: true),To put call buttons in the chat header itself, pass the icon variant to the provider. It is a widget you pass in, so the main package never carries the call code:
KerabieProvider(config: ..., callActions: const KerabieCallButton.icon(), child: ...)The button opens a call screen with the queue position, ringing, the call (mute, camera, end) and how it ended. For your own design, create a KerabieCallController and read its phase.
phase | Meaning |
|---|---|
starting | Asking Kerabie to start the call |
waiting | Every agent is busy; queuePosition is the place in line |
ringing | Free agents are being rung |
connected | Answered by a person or the AI (handler) |
ended | Over; endReason says why |
If you ask customers for their details before a chat (the pre-chat form in the dashboard), the call asks the same questions first, once. Pass user in KerabieConfig, or call setUser after login, and a customer you have identified is never asked. On a video call the customer sees the agent, with their own camera as a small picture in the corner.
A call goes to every free agent at once and the first to answer wins. When nobody is free, the AI voice agent answers if you turned it on. Otherwise the customer waits in the queue, and the call is marked missed after two minutes.
AI agent for your app
A voice agent inside your app that uses your tools as the signed-in customer, shows screens instead of reading lists, and hands over to your team with a summary. Read AI agent for your app for how tools, secure routes and screens work. It needs the same setup as calls: calls and the AI voice agent turned on in the dashboard, and the microphone permission.
import 'package:kerabie_helpdesk_flutter_calls/kerabie_helpdesk_flutter_calls.dart';
// Anywhere under KerabieProvider. It shows nothing when calls are off.
KerabieAgentButton(
name: 'Kerabie Agent', // optional, shown on the button and the call screen
getUserToken: () async => auth.currentToken, // null when nobody is signed in
screens: {
// Screens that need your data or your design. A list, a confirmation and an address box are built in.
'pick_saved_address': (payload) async => pickAddress(payload['purpose']), // the address id, or 'none' to type a new one
'pick_schedule': (payload) async => pickDateTime(), // ISO 8601 date and time
'open_screen': (payload) async { openScreen(payload['screen']); return 'opened'; },
},
)To let the agent do things in the app itself, such as adding to its own cart, pass actions: {'add_to_cart': KerabieAgentAction(description: ..., parameters: {...}, run: (args) async => ...)}. Each becomes a tool named action_<name>.
Each handler shows your screen and returns the customer's answer, which the agent reads back. Throw if the customer closes it. The agent only uses the screens you list. Addresses are never taken by voice. For your own design pass KerabieAgentOptions(getUserToken:, screens:) as agent: to KerabieCallController.
Push notifications
Kerabie keeps one push token per customer, so use one push service in your app. The SDK does not depend on a push package. You get the token from your service and pass it in, with the provider that issued it.
final kerabie = KerabieProvider.of(context, listen: false);
await kerabie.registerPushToken(token, provider: KerabiePushProvider.fcm);The providers are fcm and expo, the two values the Kerabie API accepts. fcm takes the token from FirebaseMessaging.getToken(), and it also covers iOS because Firebase delivers through Apple's service for you.
Using Firebase (fcm)? Connect your Firebase project once in the dashboard under Settings, Integrations, App push, so Kerabie can send to your app's customers. Without it, replies are not pushed to your app.
Opening the chat when a notification is tapped
Kerabie notifications carry conversationId and orgId in their data. Pass the data to the SDK when the user taps. With Firebase Cloud Messaging, handle both cases FCM documents: the tap that launches the app (getInitialMessage) and the tap from the background (onMessageOpenedApp).
final messaging = FirebaseMessaging.instance;
final initial = await messaging.getInitialMessage();
if (initial != null) kerabie.handleNotificationData(initial.data);
FirebaseMessaging.onMessageOpenedApp.listen((m) => kerabie.handleNotificationData(m.data));
messaging.onTokenRefresh.listen((t) => kerabie.registerPushToken(t, provider: KerabiePushProvider.fcm));The chat can only open from a tap if you gave KerabieProvider the same navigatorKey as your MaterialApp. With go_router, pass the same key as GoRouter(navigatorKey: navigatorKey).
Opening the chat from a link
Links use the same format in every Kerabie SDK: <your-scheme>://conversation/<id>.
Flutter handles incoming deep links itself, so the SDK does not listen for them. Hand the link to the SDK where your app receives it, for example in onGenerateRoute or your router's redirect.
if (kerabie.handleDeepLink(Uri.parse('myapp://conversation/123'))) return;KerabieDeepLink.conversationUri(123, 'myapp') builds a link you can put in a notification.
Use with an AI assistant
The Kerabie MCP server can read this SDK's documentation, so a coding assistant can add it to your app.