utd_chat_kit 0.9.2
utd_chat_kit: ^0.9.2 copied to clipboard
WhatsApp-style 1:1 & group chat for Flutter — independent UTD-Stream kit.
utd_chat_kit #
WhatsApp-style 1:1 & group chat for Flutter — an independent UTD-Stream kit. Local-first (Drift) chat with an optimistic outbox, a signalling WebSocket, group roles, presence, typing indicators, media attachments, and (v0.9) background message push + deep-linking.
Usage #
final client = await UTDChat.init(
UTDChatConfig(
appId: 'your_app_id',
appKey: 'your_app_key',
userId: 'user-123',
userName: 'Sara',
// push is on by default; pass UTDPushConfig(enabled: false) to opt out.
),
);
// Conversation list / chat thread, already scoped:
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => UTDChat.conversations(client)),
);
Capabilities & billing #
Chat is the paid chat capability (persistent messaging + history +
receipts), billed separately from signaling (calls). Both are sold via a
subscription plan (Free / Signaling / Pro / Enterprise) with per-plan limits;
billing is MAU-based — there is no per-message charge.
The session mint surfaces the chat flag + plan limits on the client (all
nullable — null when the engine didn't report it, on an older engine):
// Proactive pre-check (set on connect):
client.chatEnabled.value; // bool? — null = unknown, false = show an upsell
// Enforce caps client-side for UX + show history retention:
final limits = client.planLimits; // UTDPlanLimits?
final maxMembers = limits?.maxGroupMembers; // cap the group-member picker
final historyDays = limits?.historyDays; // "messages are kept N days"
UTDChatClient.messagingUnavailable remains the reactive, after-a-forbidden-send
signal; chatEnabled is the proactive pre-check. Absent flags never hard-break
the kit — it degrades to server-authoritative enforcement.
Media attachments (v0.6) #
The chat thread (UTDChatScreen) ships with an attach button that lets users
send photos (gallery/camera), videos, and arbitrary files. Attachments are
uploaded to the engine's R2 storage via the presign endpoint
(POST /api/v1/signal/uploads) and sent as a message of type
image/file/audio/video with metadata.url = object_url.
Required host-app configuration #
Media pickers/camera require platform permissions declared by your app:
iOS — add to ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Take photos to send in chat</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Attach photos and videos to messages</string>
<key>NSMicrophoneUsageDescription</key>
<string>Record audio/video to send in chat</string>
Android — image_picker and file_picker declare what they need; for
camera capture add to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
Engine prerequisite #
The engine project's R2/object-storage env must be provisioned. If it is not,
POST /api/v1/signal/uploads returns 503, surfaced as a
UTDStreamException with code == UTDErrorCode.uploadsUnavailable (and a
friendly snackbar in the default UI).
Headless use #
To send media without the default UI, drive UTDUploadController directly:
final uploads = UTDUploadController(
client: chatClient,
conversationId: conversationId,
peerIdentity: peerIdentity, // null for a group
);
await uploads.pickImage(); // or captureImage / pickVideo / pickFile
Background message push + deep-link (v0.9) #
The kit registers this device's FCM token with the engine after connect()
(POST /api/v1/devices/push-token, identity-bound to the minted bearer), keeps
it fresh on onTokenRefresh, and unregisters it on dispose(). Incoming
{type:'message', conversation_id, ...} pushes are handled on all three FCM
surfaces:
- Foreground (
onMessage) — shows a local-notification banner unless the user is currently viewing that conversation (then it's suppressed). - Background tap (
onMessageOpenedApp) and cold start (getInitialMessage) — deep-link to the chat screen. - Terminated/background data messages — a top-level
utdChatFirebaseBackgroundHandlerrenders a banner; tapping it deep-links.
Push is on by default; set UTDChatConfig(push: UTDPushConfig(enabled: false))
to disable all FCM wiring.
Required host-app configuration #
Push requires your app to set up Firebase and wire two hooks. The kit does NOT bundle a Firebase config (that is per-app).
1. Add Firebase to your app (Android + iOS) with the FlutterFire CLI:
dart pub global activate flutterfire_cli
flutterfire configure # writes firebase_options.dart + native config
This drops android/app/google-services.json and
ios/Runner/GoogleService-Info.plist and configures the Gradle/CocoaPods
plugins. (iOS additionally needs the Push Notifications capability and an
APNs key/cert uploaded to the Firebase project.)
2. Initialize Firebase and register the background handler before runApp:
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:utd_chat_kit/utd_chat_kit.dart';
import 'firebase_options.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
// MUST be a top-level function, registered before runApp.
FirebaseMessaging.onBackgroundMessage(utdChatFirebaseBackgroundHandler);
runApp(const MyApp());
}
3. Supply a navigation handler so a notification tap opens the right thread.
The router queues the deep-link until the client is connected, so register it
once after UTDChat.init:
client.notificationRouter.setHandler((link) {
navigatorKey.currentState?.push(MaterialPageRoute(
builder: (_) => UTDChat.chat(
client,
conversationId: link.conversationId,
peerIdentity: link.senderIdentity ?? '',
),
));
});
iOS — add to ios/Runner/Info.plist if you want the banner while the app is
foregrounded, and ensure the Push Notifications + Background Modes (Remote
notifications) capabilities are enabled in Xcode.
Android — flutter_local_notifications posts on the channel the kit creates
(utd_chat_messages by default; override via UTDPushConfig). On Android 13+
the kit requests the POST_NOTIFICATIONS permission at connect time.
Engine prerequisite (server-side gate) #
The engine project's FCM/APNs env must be provisioned. Until then, token registration still succeeds (the token is stored) but the engine sends no pushes — there is nothing to runtime-verify on the device until ops enables it.
Additional information #
This kit is independent of utd_stream_sdk. The two-host transport (mint host +
engine host), the no-backend X-App-Key mint, and the device-id model match the
sibling UTD-Stream kits.