Zukko Chat Flutter SDK
A native Flutter chat screen for Zukko Chat. Drop in one widget and your users can talk to your support team or AI agent in real time, with text, photos, files and voice messages. iOS and Android.
Styles
Pick a look with ZukkoChatOptions.style. Each style has light and dark variants.
Smooth Silk
Tonal bubbles that blend into the background, floating composer (default).

class SmoothSilkScreen extends StatelessWidget {
const SmoothSilkScreen({super.key, required this.user});
final ZukkoChatUser user; // from your login, see "User identity"
@override
Widget build(BuildContext context) {
return ZukkoChat(
title: 'Support',
options: ZukkoChatOptions(
baseUrl: const String.fromEnvironment('ZUKKO_BASE_URL'),
websiteToken: const String.fromEnvironment('ZUKKO_WEBSITE_TOKEN'),
style: ZukkoChatStyle.smoothSilk,
l10n: ZukkoChatL10n.uzLatn,
user: user,
theme: ZukkoChatTheme(brightness: Theme.of(context).brightness),
),
interactions: ZukkoChatInteractions(onMorePressed: () {}),
);
}
}
Minimal Clean
Agent replies as plain text, only the user's messages in bubbles.

class MinimalCleanScreen extends StatelessWidget {
const MinimalCleanScreen({super.key, required this.user});
final ZukkoChatUser user; // from your login, see "User identity"
@override
Widget build(BuildContext context) {
return ZukkoChat(
title: 'Support',
options: ZukkoChatOptions(
baseUrl: const String.fromEnvironment('ZUKKO_BASE_URL'),
websiteToken: const String.fromEnvironment('ZUKKO_WEBSITE_TOKEN'),
style: ZukkoChatStyle.minimalClean,
l10n: ZukkoChatL10n.uzLatn,
user: user,
theme: ZukkoChatTheme(brightness: Theme.of(context).brightness),
),
interactions: ZukkoChatInteractions(onMorePressed: () {}),
);
}
}
Thread Feed
Full-width rows with avatar and name, no bubbles.

class ThreadFeedScreen extends StatelessWidget {
const ThreadFeedScreen({super.key, required this.user});
final ZukkoChatUser user; // from your login, see "User identity"
@override
Widget build(BuildContext context) {
return ZukkoChat(
title: 'Support',
options: ZukkoChatOptions(
baseUrl: const String.fromEnvironment('ZUKKO_BASE_URL'),
websiteToken: const String.fromEnvironment('ZUKKO_WEBSITE_TOKEN'),
style: ZukkoChatStyle.threadFeed,
l10n: ZukkoChatL10n.uzLatn,
user: user,
theme: ZukkoChatTheme(brightness: Theme.of(context).brightness),
),
interactions: ZukkoChatInteractions(onMorePressed: () {}),
);
}
}
Features
- One widget (
ZukkoChat) or a headless client (ZukkoChatClient) if you build your own UI. - Three looks, each with light and dark variants:
smoothSilk(default),minimalClean,threadFeed. - Real-time messages, typing indicator, online status, connection banner, automatic reconnect with missed-message catch-up.
- Attachments (photos, files) and hold-to-record voice messages.
- Markdown in agent and AI replies (the visitor's own messages stay plain text).
- Link previews (optional).
- Secure user identity (HMAC) so a user keeps the same history on every device.
- Encrypted on-device cache of the session and the newest 20 messages (optional).
- Localization: English, Russian, Uzbek (Latin and Cyrillic), Turkish, Karakalpak.
- No storage or media permissions: the system pickers are used.
Contents
- Styles
- Requirements
- Installation
- Platform setup
- Get your baseUrl and websiteToken
- Quick start
- User identity (HMAC)
- Logout
- Configuration reference
- Callbacks and errors
- Behavior details
- Headless client
- Troubleshooting / FAQ
- Example app
- License
Requirements
| Minimum | |
|---|---|
| Flutter | 3.47 |
| Dart | 3.13 |
| iOS | 15.0 |
| Android | API 24 (Android 7.0): the minimum of the record, flutter_secure_storage and url_launcher plugins the SDK uses, and Flutter's default minSdkVersion |
Installation
Add the package:
dependencies:
zukko_chat_flutter: ^1.0.0
flutter pub get
(For local testing a path: dependency pointing at a checkout of this repository works too.)
import 'package:zukko_chat_flutter/zukko_chat_flutter.dart';
Platform setup
Android
Open android/app/src/main/AndroidManifest.xml and add, inside <manifest>:
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Only if you keep voice messages enabled (enableVoiceMessages: true). -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
The microphone permission is requested at runtime by the system the first time the user records.
iOS
-
In
ios/Podfilemake sure the platform is at least 15.0:platform :ios, '15.0' -
In
ios/Runner/Info.plistadd the microphone usage text (only needed when voice messages are enabled; without it iOS terminates the app when recording starts):<key>NSMicrophoneUsageDescription</key> <string>Record voice messages in chat.</string> -
Run
cd ios && pod install(or justflutter run).
No storage permissions
The SDK never needs READ_MEDIA_* or READ/WRITE_EXTERNAL_STORAGE. Photos and files are chosen through the system pickers.
If you do not use voice messages, set enableVoiceMessages: false and skip the microphone entries.
Get your baseUrl and websiteToken
- Open the Zukko Chat admin panel.
- Go to Settings, then Inboxes, then your website inbox, then Configuration.
- The web widget snippet shown there contains both values:
websiteTokenandBASE_URL.
Rules:
baseUrlmust behttps://.... Plainhttp, even for localhost, is rejected (anArgumentErroris thrown).websiteTokenis public. It identifies the inbox and is not a secret.
Keep both outside your source with --dart-define:
flutter run \
--dart-define=ZUKKO_BASE_URL=https://chat.example.com \
--dart-define=ZUKKO_WEBSITE_TOKEN=your-website-token
const baseUrl = String.fromEnvironment('ZUKKO_BASE_URL');
const websiteToken = String.fromEnvironment('ZUKKO_WEBSITE_TOKEN');
Quick start
Minimal, anonymous visitor:
import 'package:flutter/material.dart';
import 'package:zukko_chat_flutter/zukko_chat_flutter.dart';
void openSupport(BuildContext context) {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const ZukkoChat(
options: ZukkoChatOptions(
baseUrl: 'https://chat.example.com',
websiteToken: 'your-website-token',
),
),
),
);
}
ZukkoChat is a full-screen page. It creates the client, connects, loads history and disposes everything when it leaves the tree. A back button is shown automatically when the page can be popped.
Full example with every parameter:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:zukko_chat_flutter/zukko_chat_flutter.dart';
const baseUrl = String.fromEnvironment('ZUKKO_BASE_URL');
const websiteToken = String.fromEnvironment('ZUKKO_WEBSITE_TOKEN');
Widget buildChat({required String userId, required String userHash}) {
return ZukkoChat(
title: 'Support', // header title; default is the localized "Support"
options: ZukkoChatOptions(
baseUrl: baseUrl, // https only
websiteToken: websiteToken, // inbox token (public)
enablePersistence: true, // encrypted on-device cache; false = memory only
user: ZukkoChatUser(
identifier: userId, // stable and unique per user in your system
identifierHash: userHash, // HMAC computed by YOUR backend
name: 'Aziz Karimov',
email: 'aziz@example.com',
phoneNumber: '+998901234567', // E.164
avatarUrl: 'https://example.com/avatars/aziz.png',
customAttributes: {'plan': 'pro'},
),
style: ZukkoChatStyle.smoothSilk, // smoothSilk | minimalClean | threadFeed
theme: const ZukkoChatTheme(
brightness: Brightness.light, // light or dark variant
accentColor: Color(0xFF0A7AFF), // send button, links
horizontalPadding: 16,
),
l10n: ZukkoChatL10n.uzLatn, // omit to follow the app locale
timeFormat: DateFormat.Hm(), // message time format
enableAttachments: true, // photo / file button
enableVoiceMessages: true, // hold-to-record microphone
enableLinkPreviews: true, // fetch preview cards for links
),
callbacks: ZukkoChatCallbacks(
onError: (e) => debugPrint('chat error: ${e.type.wire} ${e.cause}'),
onMessageReceived: (m) => debugPrint('new message from an agent'),
onConnectionStateChanged: (s) => debugPrint('connection: $s'),
),
interactions: ZukkoChatInteractions(
onMessageTap: (message) {},
onSendPressed: (text) {},
onMorePressed: () {}, // shows the header menu button
),
);
}
User identity (HMAC)
Important
Identity validation (HMAC). If "Enforce identity validation" is turned on for your inbox in Zukko Chat, every user you pass with an identifier also needs a correct identifierHash. Without it the chat does not open: the screen shows "Your account could not be verified", onError receives INVALID_IDENTIFIER_HASH, and the debug console prints exactly one line:
[zukko_chat] ERROR: Identity validation is enabled for this inbox, but identifierHash is missing for user.identifier. Compute HMAC-SHA256(<inbox HMAC token>, identifier) on your backend and pass it as ZukkoChatUser.identifierHash.
It says wrong instead of missing when a hash was sent but does not match. If the setting is off and you send no hash, the SDK stays silent and the chat works.
What you need to generate the hash
- The inbox HMAC token: Zukko Chat admin → Settings → Inboxes → your inbox → Configuration → "Identity validation". Use the inbox that
websiteTokenbelongs to. - The exact
identifierstring your app passes (same case, no spaces;42and"42 "are different). - For a quick test:
openssl(preinstalled on macOS and Linux; on Windows use Git Bash).
Generate it (testing only)
printf '%s' 'user-42' | openssl dgst -sha256 -hmac 'YOUR_HMAC_TOKEN'
Copy only the 64-character hex value (ignore a SHA2-256(stdin)= or (stdin)= prefix) into identifierHash. Use printf or echo -n: a plain echo adds a newline and produces a different hash.
If you see the ERROR
missing: passidentifierHashtogether withidentifier.wrong: check that the hash was computed over the sameidentifier, with the HMAC token of the same inbox, as lowercase hex, without a trailing newline, and that the token was not regenerated in the admin (that invalidates all old hashes).
In production compute the hash on your backend and return it with your login response (see How to produce it). Never put the HMAC token in the app.
Without a user the visitor is anonymous. To tie the chat to one of your users, pass a ZukkoChatUser:
| Field | Type | What it does |
|---|---|---|
identifier |
String? |
Your id for the user. Must be stable and unique per user (user id, not email that can change). |
identifierHash |
String? |
Proof of identity: hex HMAC-SHA256 of identifier, computed on your backend. |
name |
String? |
Display name shown to agents. |
email |
String? |
Email shown to agents. |
phoneNumber |
String? |
Phone in E.164 format, for example +998901234567. |
avatarUrl |
String? |
URL of the profile picture. |
customAttributes |
Map<String, dynamic>? |
Any extra attributes attached to the contact. |
identifierHash and customAttributes are never written to the device.
Why the hash matters
- It proves the user is who they say. Without it anyone could send someone else's
identifier, appear as that customer to agents and the AI, and overwrite their name, email and phone. - With a verified identity the same user sees the same history on a new phone, after reinstall, and after logout then login. Without a hash each device starts with an empty conversation.
How to produce it
- Find the HMAC token: Zukko Chat admin, Settings, Inboxes, your inbox, Configuration, "Identity validation".
- Compute
HMAC-SHA256(hmacToken, identifier)as a hex string on your backend. The HMAC token must never be shipped in the app or fetched by it. - Return the hash to the app with your login response and pass it as
identifierHash.
Node.js:
const crypto = require('crypto');
const hash = crypto.createHmac('sha256', process.env.ZUKKO_HMAC_TOKEN)
.update(String(user.id)).digest('hex');
Python:
import hmac, hashlib
h = hmac.new(HMAC_TOKEN.encode(), str(user.id).encode(), hashlib.sha256).hexdigest()
PHP:
$hash = hash_hmac('sha256', (string) $user->id, $hmacToken);
Quick check from a terminal:
printf '%s' 'user-42' | openssl dgst -sha256 -hmac 'YOUR_HMAC_TOKEN'
"Enforce identity validation" setting
| Inbox setting | Hash | Result |
|---|---|---|
| On | missing or wrong | The chat does not open. A banner shows "Your account could not be verified" (localized), onError receives INVALID_IDENTIFIER_HASH, and exactly one ERROR line is written to the debug log. No anonymous fallback. |
| On | correct | Works. |
| Off | missing | Works silently: no onError, no log line (but no cross-device history). |
| Off | correct | Works. |
Session token
The SDK obtains the session token (JWT) itself and keeps it encrypted in the local database; the encryption key lives in the iOS Keychain / Android Keystore. Your app never handles or sees it.
Logout
When your user logs out:
- Close the chat (pop the
ZukkoChatpage, or calldispose()on a headless client). - Forget the user's local data:
await zukkoChatSignOut(
baseUrl: baseUrl,
websiteToken: websiteToken,
user: currentUser, // the same ZukkoChatUser passed to the chat
);
This removes the cached session, conversation and messages of that identifier, whatever hash it was stored with. Other users and inboxes are untouched. After the next login with a correct hash the user gets the server-side history back.
If the chat was ever opened without a user (anonymous), wipe everything instead:
await ZukkoChatClient.clearAllData();
Both can be called before any chat was created.
Configuration reference
ZukkoChat
| Parameter | Type | Default | What it does |
|---|---|---|---|
options |
ZukkoChatOptions |
required | Connection and look, see below. |
title |
String? |
localized header title | Title in the chat header. |
callbacks |
ZukkoChatCallbacks? |
none | Client events, see Callbacks. |
interactions |
ZukkoChatInteractions |
all empty | UI events, see below. |
ZukkoChatOptions
| Parameter | Type | Default | What it does |
|---|---|---|---|
baseUrl |
String |
required | Your Zukko Chat server, https only. |
websiteToken |
String |
required | Token of the website inbox. |
enablePersistence |
bool |
true |
true: session and the newest 20 messages are cached encrypted on the device. false: memory only, a new session on every open. |
user |
ZukkoChatUser? |
null |
Identity of the user; null is an anonymous visitor. |
style |
ZukkoChatStyle |
smoothSilk |
smoothSilk: tonal bubbles and a floating composer. minimalClean: agent replies as plain text, only the user's messages in bubbles. threadFeed: full-width rows with avatar and name, no bubbles. |
theme |
ZukkoChatTheme? |
see below | Brightness and overrides. |
l10n |
ZukkoChatL10n? |
by app locale | Texts. null picks the built-in translation of the ambient locale (English if none). |
timeFormat |
DateFormat? |
DateFormat.Hm() |
Format of the time on messages. |
enableAttachments |
bool |
true |
Show the attachment button. |
enableVoiceMessages |
bool |
true |
Show the hold-to-record microphone. Needs the microphone entries from Platform setup. |
enableLinkPreviews |
bool |
true |
Preview card (title, description, image) for the first link of a message. If the server did not send preview data, the device itself does a plain GET of the http(s) link (no cookies, no SDK headers, no session token; at most 3 redirects, 5 s, first 512 KB of HTML) and reads the Open Graph tags; the preview image, if any, must be https and is loaded from that site too. This reveals the device's IP address to the linked site; turn off for privacy. Without it a link still shows its domain. |
If options changes with the same baseUrl, websiteToken, enablePersistence and user, the running connection is kept; changing any of those starts a new client.
ZukkoChatTheme
If options.theme is null, the theme registered in ThemeData.extensions is used, else one derived from the ambient ThemeData (so dark mode follows your app).
| Parameter | Type | Default | What it does |
|---|---|---|---|
brightness |
Brightness |
required | Light or dark variant of the style. |
fontFamily |
String? |
style's own font | Forces one font on all chat text. Bundled: Manrope (smoothSilk), Geist (minimalClean), Onest (threadFeed). |
accentColor |
Color? |
style's own | Overrides the accent (send button, links). |
backgroundColor |
Color? |
style's own | Overrides the chat background. |
horizontalPadding |
double |
16 |
Gap between the screen edge and messages / composer. |
Helpers: ZukkoChatTheme.fromColorScheme(scheme), ZukkoChatTheme.fromThemeData(theme).
ZukkoChatInteractions
| Parameter | Type | What it does |
|---|---|---|
onEndReached |
Future<void> Function()? |
Called when the top of the list (oldest message) is reached. Older history loads by itself; this is only a hook. |
onEndReachedThreshold |
double? |
Fraction (0..1) of the list at which onEndReached (and the built-in loading of older messages) triggers. Default 0.01, that is, at the very top. |
onMessageTap |
void Function(Message)? |
A message was tapped. |
onMessageLongPress |
void Function(BuildContext, Message)? |
A message was long-pressed. |
onSendPressed |
void Function(String text)? |
Called with the text the user sent. |
onMorePressed |
VoidCallback? |
Menu button of the header; the button is hidden when null. |
Localization
Built in: ZukkoChatL10n (English, the default), ZukkoChatL10n.ru, .uzLatn, .uzCyrl, .tr, .kaa (Karakalpak). ZukkoChatL10n.forLocale(locale) picks one (Uzbek is Latin unless the locale script is Cyrl). Leave l10n null to follow the app locale automatically, or pass one explicitly. Every text of the UI is a field of ZukkoChatL10n, so you can build your own with the constructor.
Limits (ZukkoChatUpload)
| Limit | Value |
|---|---|
| Message text | 150000 characters (ZukkoChatUpload.maxMessageLength) |
| Attachments per message | 15 (maxAttachments) |
| Size per attachment | 40 MB (maxSizeMb) |
| Accepted types | any image, video and audio, plus PDF, Office and OpenDocument documents, text/CSV/RTF/JSON, ZIP / 7z / RAR / TAR (acceptedDocumentTypes) |
| Voice message length | 5 minutes |
The UI shows a localized message when a limit is hit.
Callbacks and errors
ZukkoChatCallbacks
All optional. A callback that throws never breaks the SDK.
| Callback | Signature | Fires when |
|---|---|---|
onWelcome |
() |
Realtime connection greeted the client. |
onPing |
() |
Server keep-alive ping. |
onConfirmedSubscription |
() |
Realtime subscription confirmed. |
onConnectionStateChanged |
(ZukkoChatConnectionState) |
The realtime connection changed state: idle, connecting, connected, subscribed, backoff, closed. Missed messages are refetched automatically once it is subscribed again. |
onMessageReceived |
(ZukkoChatMessage) |
A message from someone else (agent, AI) arrived. |
onMessageSent |
(ZukkoChatMessage, String echoId) |
Your sent message was accepted by the server. |
onMessageDelivered |
(ZukkoChatMessage, String) |
A message created by the current user came back over realtime. |
onMessageUpdated |
(ZukkoChatMessage) |
A message changed. |
onPersistedMessagesRetrieved |
(List<ZukkoChatMessage>) |
Cached messages were read from the device. |
onMessagesRetrieved |
(List<ZukkoChatMessage>) |
Messages were fetched from the server. |
onConversationStartedTyping / onConversationStoppedTyping |
() |
An agent started typing / stopped (typing-off event, the agent's message arrived, the conversation was resolved, or 30 s without another typing event). Fires only on changes, not on every event. |
onConversationIsOnline / onConversationIsOffline |
() |
Presence update from the server: online when an agent is online, offline otherwise or after 40 s without an online update. Fired on each presence update, not only on change. |
onConversationResolved |
() |
The conversation's status became resolved through a realtime event (once per resolve). The visitor can keep writing. |
onConversationUpdated |
(ZukkoChatConversation) |
The current conversation is known or changed: once after the conversation is fetched at start (also after a session reset or a late start; not when the contact has no conversation yet), and on each realtime conversation.created / updated / status_changed event of the contact's inbox. |
onSessionReset |
() |
The old session was replaced by a new contact; shown messages are dropped and the latest page is fetched. |
onError |
(ZukkoChatException) |
Any failure, see below. |
Errors
ZukkoChatException has cause (human text, never secrets), type (ZukkoChatExceptionType), data (for a failed send: the message's echoId), and optional error / stackTrace. type.wire is a stable string code.
| Type | Wire code | When |
|---|---|---|
getConfigFailed |
GET_CONFIG_FAILED |
Starting the session (/config) failed or returned an unusable answer. |
createClientFailed |
CREATE_CLIENT_FAILED |
The client could not be created. |
invalidIdentifierHash |
INVALID_IDENTIFIER_HASH |
The inbox enforces identity validation and the hash is missing or wrong. Terminal: no anonymous fallback, no realtime. |
identifyFailed |
IDENTIFY_FAILED |
Attaching the user failed (server error, timeout, 404, 422). The session is not usable (no sends, no realtime); the next start retries. For a user without identifier it is only a warning. |
sessionExpired |
SESSION_EXPIRED |
The session could not be kept alive (server keeps rejecting the token or the widget is unavailable). |
socketSubscriptionRejected |
SOCKET_SUBSCRIPTION_REJECTED |
The server did not accept the realtime subscription. |
socketDisconnected |
SOCKET_DISCONNECTED |
The server closed the realtime connection and asked not to reconnect. |
getMessagesFailed |
GET_MESSAGES_FAILED |
Loading messages failed. |
getConversationFailed |
GET_CONVERSATION_FAILED |
Loading the conversation failed. |
sendMessageFailed |
SEND_MESSAGE_FAILED |
Sending failed; data is the echoId. |
updateContactFailed |
UPDATE_CONTACT_FAILED |
Updating the contact failed. |
storageFailed |
STORAGE_FAILED |
Local storage could not be opened. The chat keeps working without persistence. |
identityNotVerified |
IDENTITY_NOT_VERIFIED |
No longer emitted; kept so existing switch code compiles. |
Behavior details
- History paging. On open the newest 20 messages are loaded; older pages load automatically as the user scrolls to the top. Only the newest 20 are cached on the device; older pages are kept in memory only and are fetched again on the next open. The app has nothing to do.
- Auto-scroll. A new message scrolls the list to the end when it is the user's own, or when the user is within about one screen of the end. A user reading far up history is not moved.
- Markdown. Agent and AI messages render Markdown; the visitor's own messages are always plain text.
- Connection banner. Offline and reconnecting states are shown in the chat; on reconnect the SDK refetches what was missed.
- Footer. The "Powered by zukko.ai" line under the composer is always shown and not configurable.
- Voice. The microphone button appears only when
enableVoiceMessagesis true and the inbox allows voice messages (audio_messaging_enabledis not false). - Debug log. In debug builds the SDK prints lines prefixed
[zukko_chat]at three levels:ERROR:(something you must fix, for example a wrong identity hash),WARNING:(recoverable) and plain info. Release builds print nothing. Tokens, JWTs and hashes are never logged.
Headless client
Use ZukkoChatClient to build your own UI or to react to messages without a screen.
final client = await ZukkoChatClient.create(
baseUrl: baseUrl,
websiteToken: websiteToken,
user: user,
enablePersistence: true,
callbacks: ZukkoChatCallbacks(
onMessageReceived: (m) => debugPrint('new message'),
onError: (e) => debugPrint(e.toString()),
),
);
await client.loadMessages();
await client.sendMessage(content: 'Hello', echoId: 'tmp-1');
// ...
await client.dispose();
| Member | What it does |
|---|---|
ZukkoChatClient.create({baseUrl, websiteToken, user, enablePersistence, callbacks}) |
Connects (session, user, conversation). Throws ArgumentError for an invalid or non-https baseUrl; other failures arrive through onError. |
config |
Inbox branding and behavior (ZukkoChatConfig), null until the session exists. |
loadMessages() |
Loads cached then remote messages; fires the retrieved callbacks. |
loadOlderMessages({required int before}) |
Up to 20 messages older than message id before, oldest first; fewer than 20 means no more. Returns null on failure. |
sendMessage({content, echoId, attachments, replyTo}) |
Sends text and/or ZukkoChatUpload attachments. echoId is your temporary id, returned in onMessageSent. |
setTyping(bool) / markSeen() |
Typing indicator / read receipt (best effort). |
resume() |
Call when the app returns to the foreground to restart realtime if it stopped. |
sendAction(ZukkoChatActionType) |
Sends a presence action; false if the connection is not subscribed. |
clearClientData() |
Clears this client's conversation and messages (contact kept). |
dispose() |
Stops timers and sockets. Safe to call twice. |
ZukkoChatClient.clearData(...) / clearAllData() |
Static wipe of persisted data (what zukkoChatSignOut uses). |
Troubleshooting / FAQ
The chat shows "Your account could not be verified".
Identity validation is enforced on the inbox and identifierHash is missing or wrong. Compute HMAC-SHA256 of the exact identifier string with the inbox HMAC token on your backend, hex-encoded. The debug log says which one it is ("missing" or "wrong").
ArgumentError about baseUrl.
Only https:// is accepted, including for local development. Use an https tunnel or certificate for a dev server.
The microphone button is missing.
Either enableVoiceMessages is false, or voice messages are disabled for the inbox on the server (audio_messaging_enabled: false).
The app crashes (iOS) or recording fails when I tap the microphone.
Add NSMicrophoneUsageDescription (iOS) and RECORD_AUDIO (Android), see Platform setup.
A user has no history on a new phone.
Without a valid identifierHash each device is a separate visitor. Pass the hash from your backend.
The chat is empty after logout and login.
Expected: zukkoChatSignOut removes the local cache. With a verified identity the history comes back from the server on the next login.
No debug output in release. By design, logging is debug-only.
I need to see what the SDK does.
Run a debug build and filter the console for [zukko_chat].
Example app
A runnable app lives in example/: example/lib/main.dart and the live chat screen example/lib/live/live_chat_screen.dart. How to run it and where to get each value: example/README.md. On pub.dev see the "Example" tab.
cd example
flutter run \
--dart-define=ZUKKO_BASE_URL=https://chat.example.com \
--dart-define=ZUKKO_WEBSITE_TOKEN=your-website-token \
--dart-define=ZUKKO_USER_ID=user-42 \
--dart-define=ZUKKO_USER_HASH=hex-hmac-of-user-id
ZUKKO_USER_ID and ZUKKO_USER_HASH are optional; without them the visitor is anonymous.
License
MIT, see LICENSE.
Libraries
- zukko_chat_flutter
- Zukko Chat Flutter SDK. Everything not exported here is internal.