zendesk_messaging
Zendesk Messaging SDK용 Flutter 플러그인입니다. 다중 대화 지원, 실시간 이벤트 및 JWT 인증을 제공하는 앱 내 고객 지원 메시징 기능을 제공합니다.
Flutter 개발자를 위한 Zendesk-Messaging
{"sdk":"flutter"}{"sdk":"flutter"}^4.0.0아래는 영문 원문 스냅샷입니다. 최신 내용은 GitHub에서 확인하세요.
pub package CI coverage License: MIT
Zendesk Messaging
English | 繁體中文 | 简体中文 | Español | Português (Brasil) | 日本語 | 한국어
A Flutter plugin for integrating Zendesk Messaging SDK into your mobile applications. Provides in-app customer support messaging with multi-conversation support, real-time events, and JWT authentication.
| Platform | Minimum Version |
|---|---|
| iOS | 14.0 |
| Android | API 21 (minSdk) |
| Dart | 3.6.0 |
| Flutter | 3.27.0 |
Add zendesk_messaging to your pubspec.yaml:
dependencies:
zendesk_messaging: <latest_version>
Add the Zendesk Maven repository to your project-level android/build.gradle:
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://zendesk.jfrog.io/artifactory/repo' }
}
}
Update your ios/Podfile to target iOS 14.0:
platform :ios, '14.0'
Then run:
cd ios && pod install
This plugin also supports Swift Package Manager. If your app has migrated to SPM, no extra setup is required — just make sure your app's iOS deployment target is at least 14.0. CocoaPods remains fully supported, so existing apps need no changes.
Before initializing the SDK, you need to obtain your Android and iOS channel keys from the Zendesk Admin Center:
Note: The same Channel ID is used for both platforms. You can create separate channels for Android and iOS if needed.
import 'package:zendesk_messaging/zendesk_messaging.dart';
// Initialize the SDK (call once at app startup)
await ZendeskMessaging.initialize(
androidChannelKey: '<YOUR_ANDROID_CHANNEL_KEY>',
iosChannelKey: '<YOUR_IOS_CHANNEL_KEY>',
);
// Show the default messaging interface
await ZendeskMessaging.show();
// Show a specific conversation (requires multi-conversation enabled)
await ZendeskMessaging.showConversation('conversation_id');
// Show the conversation list
await ZendeskMessaging.showConversationList();
// Start a new conversation
await ZendeskMessaging.startNewConversation();
// Login with JWT
try {
final response = await ZendeskMessaging.loginUser(jwt: '<YOUR_JWT_TOKEN>');
print('Logged in: ${response.id}');
} catch (e) {
print('Login failed: $e');
}
// Check login status
final isLoggedIn = await ZendeskMessaging.isLoggedIn();
// Get current user
final user = await ZendeskMessaging.getCurrentUser();
if (user != null) {
print('User ID: ${user.id}');
print('External ID: ${user.externalId}');
print('Auth type: ${user.authenticationType.name}');
}
// Logout
await ZendeskMessaging.logoutUser();
The SDK provides a unified event stream for all Zendesk events. Use Dart 3 pattern matching to handle specific event types:
ZendeskMessaging.eventStream.listen((event) {
switch (event) {
case UnreadMessageCountChanged(:final totalUnreadCount, :final conversationId):
print('Unread: $totalUnreadCount${conversationId != null ? ' (conversation: $conversationId)' : ''}');
case AuthenticationFailed(:final errorMessage, :final isJwtExpired):
print('Auth failed: $errorMessage (JWT expired: $isJwtExpired)');
if (isJwtExpired) {
// Refresh JWT token
}
case ConnectionStatusChanged(:final status):
print('Connection: ${status.name}');
case ConversationAdded(:final conversationId):
print('Conversation added: $conversationId');
case ConversationStarted(:final conversationId):
print('Conversation started: $conversationId');
case ConversationOpened(:final conversationId):
print('Conversation opened: ${conversationId ?? 'default'}');
case MessagesShown(:final conversationId, :final messages):
print('Messages shown: ${messages.length} in $conversationId');
case SendMessageFailed(:final errorMessage):
print('Send failed: $errorMessage');
case FieldValidationFailed(:final errors):
print('Field validation failed: ${errors.join(', ')}');
case MessagingOpened():
print('Messaging UI opened');
case MessagingClosed():
print('Messaging UI closed');
case ProactiveMessageDisplayed(:final proactiveMessageId):
print('Proactive message displayed: $proactiveMessageId');
case ProactiveMessageClicked(:final proactiveMessageId):
print('Proactive message clicked: $proactiveMessageId');
case ConversationWithAgentRequested(:final conversationId):
print('Agent requested: $conversationId');
case ConversationWithAgentAssigned(:final conversationId):
print('Agent assigned: $conversationId');
case ConversationServedByAgent(:final conversationId, :final agentId):
print('Agent serving: $agentId in $conversationId');
case NewConversationButtonClicked():
print('New conversation button clicked');
case PostbackButtonClicked(:final actionName):
print('Postback clicked: $actionName');
case ArticleClicked(:final articleUrl):
print('Article clicked: $articleUrl');
case ArticleBrowserClicked(:final articleUrl):
print('Article opened in browser: $articleUrl');
case ConversationExtensionOpened(:final extensionUrl):
print('Extension opened: $extensionUrl');
case ConversationExtensionDisplayed(:final extensionUrl):
print('Extension displayed: $extensionUrl');
case NotificationDisplayed(:final conversationId):
print('Notification displayed: $conversationId');
case NotificationOpened(:final conversationId):
print('Notification opened: $conversationId');
}
});
// Start listening for events
await ZendeskMessaging.listenUnreadMessages();
| Event | Description |
|---|---|
UnreadMessageCountChanged |
Unread message count changed |
AuthenticationFailed |
Authentication failed |
ConnectionStatusChanged |
Connection status changed |
ConversationAdded |
New conversation created |
ConversationStarted |
Conversation initiated |
ConversationOpened |
Conversation opened |
MessagesShown |
Messages rendered |
SendMessageFailed |
Message send failed |
FieldValidationFailed |
Field validation failed |
MessagingOpened |
Messaging UI opened |
MessagingClosed |
Messaging UI closed |
ProactiveMessageDisplayed |
Proactive message shown |
ProactiveMessageClicked |
Proactive message clicked |
ConversationWithAgentRequested |
User requested agent |
ConversationWithAgentAssigned |
Agent assigned |
ConversationServedByAgent |
Agent serving |
NewConversationButtonClicked |
New conversation clicked |
PostbackButtonClicked |
Postback button clicked |
ArticleClicked |
Article clicked |
ArticleBrowserClicked |
Article opened in browser |
ConversationExtensionOpened |
Extension opened |
ConversationExtensionDisplayed |
Extension displayed |
NotificationDisplayed |
Push notification shown |
NotificationOpened |
Push notification opened |
// Get current count
final count = await ZendeskMessaging.getUnreadMessageCount();
// Get count for specific conversation
final convCount = await ZendeskMessaging.getUnreadMessageCountForConversation('conv_id');
// Listen to count changes (legacy API)
ZendeskMessaging.unreadMessagesCountStream.listen((count) {
print('Unread: $count');
});
// Set tags (applied when user sends a message)
await ZendeskMessaging.setConversationTags(['vip', 'mobile', 'flutter']);
// Clear tags
await ZendeskMessaging.clearConversationTags();
// Set custom fields
await ZendeskMessaging.setConversationFields({
'app_version': '3.0.0',
'platform': 'flutter',
'user_tier': 'premium',
});
// Clear fields
await ZendeskMessaging.clearConversationFields();
final status = await ZendeskMessaging.getConnectionStatus();
// Handle different connection states
final color = switch (status) {
ZendeskConnectionStatus.connected ||
ZendeskConnectionStatus.connectedRealtime => Colors.green,
ZendeskConnectionStatus.connectingRealtime => Colors.orange,
ZendeskConnectionStatus.disconnected => Colors.red,
ZendeskConnectionStatus.unknown => Colors.grey,
};
Override the device's system locale so the Zendesk messaging UI matches your app's language. The Zendesk SDK ships with 33 languages.
// Best: set locale before initialization
await ZendeskMessaging.setLocale('es');
await ZendeskMessaging.initialize(
androidChannelKey: '<YOUR_ANDROID_CHANNEL_KEY>',
iosChannelKey: '<YOUR_IOS_CHANNEL_KEY>',
);
// Android: can also switch at runtime before show()
await ZendeskMessaging.setLocale('ja');
await ZendeskMessaging.show();
// iOS runtime switch: requires re-initialization
await ZendeskMessaging.setLocale('fr');
await ZendeskMessaging.invalidate();
await ZendeskMessaging.initialize(
androidChannelKey: '<YOUR_ANDROID_CHANNEL_KEY>',
iosChannelKey: '<YOUR_IOS_CHANNEL_KEY>',
);
Platform details:
Locale.setDefault() and updates application/activity resource configuration. The SDK resolves UI strings from Android's resource system, so this takes effect when the SDK launches its messaging Activity. Can switch at runtime.AppleLanguages user default, which controls which localization bundle the SDK loads. Must be set before initialize(). To change after initialization, call invalidate() then initialize() again.Enable push notifications to notify users of new messages when the app is in the background or closed.
| Platform | Requirement |
|---|---|
| Android | Firebase Cloud Messaging (FCM) setup |
| iOS | APNs certificate uploaded to Zendesk Admin Center |
| Both | Real device (simulators don't support push) |
firebase_messaging to your pubspec.yaml:dependencies:
firebase_messaging: ^15.0.0
.p12 file from Keychain Accessimport 'dart:io' show Platform;
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:zendesk_messaging/zendesk_messaging.dart';
Future<void> setupPushNotifications() async {
final messaging = FirebaseMessaging.instance;
// Request permission
await messaging.requestPermission();
// Get and register the correct token per platform
// - Android: FCM token via getToken()
// - iOS: APNs device token via getAPNSToken()
// (Zendesk iOS SDK requires APNs token, NOT the FCM token)
await _registerPushToken(messaging);
// Listen for token refresh
// - Android: onTokenRefresh returns new FCM tokens
// - iOS: APNs tokens rarely change (device restore, OS update);
// re-fetch via getAPNSToken() on each app launch (done above).
// onTokenRefresh returns FCM tokens which are NOT usable here.
if (Platform.isAndroid) {
messaging.onTokenRefresh.listen((token) {
ZendeskMessaging.updatePushNotificationToken(token);
});
}
// Handle foreground notifications
FirebaseMessaging.onMessage.listen((message) async {
final responsibility = await ZendeskMessaging.shouldBeDisplayed(message.data);
switch (responsibility) {
case ZendeskPushResponsibility.messagingShouldDisplay:
await ZendeskMessaging.handleNotification(message.data);
case ZendeskPushResponsibility.notFromMessaging:
// Handle your own notification
break;
default:
break;
}
});
// Handle notification tap (app in background)
FirebaseMessaging.onMessageOpenedApp.listen((message) async {
await ZendeskMessaging.handleNotificationTap(message.data);
});
}
Future<void> _registerPushToken(FirebaseMessaging messaging) async {
String? token;
if (Platform.isAndroid) {
token = await messaging.getToken();
} else if (Platform.isIOS) {
token = await messaging.getAPNSToken();
}
if (token != null) {
await ZendeskMessaging.updatePushNotificationToken(token);
}
}
Important (iOS): The Zendesk iOS SDK uses APNs directly for push notifications, not FCM. You must pass the APNs device token via
getAPNSToken(), not the FCM registration token fromgetToken(). Using the wrong token type will silently fail to deliver notifications. APNs tokens rarely change, but callinggetAPNSToken()on each app launch ensures the token stays current.
| Method | Returns | Description |
|---|---|---|
updatePushNotificationToken(token) |
Future<void> |
Register FCM/APNs token with Zendesk |
shouldBeDisplayed(data) |
Future<ZendeskPushResponsibility> |
Check if notification is from Zendesk |
handleNotification(data) |
Future<bool> |
Display the notification |
handleNotificationTap(data) |
Future<void> |
Handle notification tap |
| Value | Description |
|---|---|
messagingShouldDisplay |
Zendesk notification, SDK can display it |
messagingShouldNotDisplay |
Zendesk notification, but should not display (e.g., user is viewing the conversation) |
notFromMessaging |
Not a Zendesk notification, handle it yourself |
unknown |
Unable to determine |
// Check if SDK is initialized
final isInit = await ZendeskMessaging.isInitialized();
// Invalidate SDK instance (cleanup)
await ZendeskMessaging.invalidate();
// After invalidate, you must call initialize() again to use the SDK
| Method | Returns | Description |
|---|---|---|
initialize(androidChannelKey, iosChannelKey) |
Future<void> |
Initialize the SDK |
isInitialized() |
Future<bool> |
Check if SDK is initialized |
invalidate() |
Future<void> |
Invalidate SDK instance |
show() |
Future<void> |
Show messaging UI |
showConversation(id) |
Future<void> |
Show specific conversation |
showConversationList() |
Future<void> |
Show conversation list |
startNewConversation() |
Future<void> |
Start new conversation |
loginUser(jwt) |
Future<ZendeskLoginResponse> |
Login with JWT |
logoutUser() |
Future<void> |
Logout current user |
isLoggedIn() |
Future<bool> |
Check login status |
getCurrentUser() |
Future<ZendeskUser?> |
Get current user info |
getUnreadMessageCount() |
Future<int> |
Get total unread count |
getUnreadMessageCountForConversation(id) |
Future<int> |
Get unread count for conversation |
listenUnreadMessages() |
Future<void> |
Start listening for events |
setConversationTags(tags) |
Future<void> |
Set conversation tags |
clearConversationTags() |
Future<void> |
Clear conversation tags |
setConversationFields(fields) |
Future<void> |
Set custom fields |
clearConversationFields() |
Future<void> |
Clear custom fields |
getConnectionStatus() |
Future<ZendeskConnectionStatus> |
Get connection status |
setLocale(locale) |
Future<void> |
Set messaging UI locale |
updatePushNotificationToken(token) |
Future<void> |
Register push token |
shouldBeDisplayed(data) |
Future<ZendeskPushResponsibility> |
Check notification source |
handleNotification(data) |
Future<bool> |
Handle push notification |
handleNotificationTap(data) |
Future<void> |
Handle notification tap |
| Stream | Type | Description |
|---|---|---|
eventStream |
Stream<ZendeskEvent> |
All Zendesk events |
unreadMessagesCountStream |
Stream<int> |
Unread count changes (legacy) |
ZendeskUser
class ZendeskUser {
String? id;
String? externalId;
ZendeskAuthenticationType authenticationType;
}
ZendeskLoginResponse
class ZendeskLoginResponse {
String? id;
String? externalId;
}
ZendeskMessage
class ZendeskMessage {
String id;
String conversationId;
String? authorId;
String? content;
DateTime? timestamp;
}
ZendeskAuthenticationType
anonymousjwtZendeskConnectionStatus
connectedconnectedRealtimeconnectingRealtimedisconnectedunknownZendeskPushResponsibility
messagingShouldDisplaymessagingShouldNotDisplaynotFromMessagingunknownpubspec.yaml:environment:
sdk: ^3.6.0
flutter: ">=3.27.0"
platform :ios, '14.0'
// Old way (still works)
ZendeskMessaging.unreadMessagesCountStream.listen((count) {
print('Unread: $count');
});
// New way (recommended)
ZendeskMessaging.eventStream.listen((event) {
if (event is UnreadMessageCountChanged) {
print('Unread: ${event.totalUnreadCount}');
}
});
Ensure your Podfile has the correct platform version:
platform :ios, '14.0'
Then run:
cd ios && pod install --repo-update
Ensure the Zendesk Maven repository is added:
maven { url 'https://zendesk.jfrog.io/artifactory/repo' }
Make sure to call listenUnreadMessages() after initialization:
await ZendeskMessaging.initialize(...);
await ZendeskMessaging.listenUnreadMessages();
| Plugin | Android SDK | iOS SDK |
|---|---|---|
| 3.1.0 | 2.36.1 | 2.36.0 |
| 3.0.0 | 2.36.1 | 2.36.0 |
| 2.9.x | 2.26.0 | 2.24.0 |
MIT License - see LICENSE for details.
Contributions are welcome! Please read the contributing guidelines before submitting a pull request.