BaxCloud Chat UI Kit
Flutter UI kit for room-based realtime chat — team chat, live stream comments, compact embeds, and bring-your-own design.
Package: baxcloud_chat_uikit_sdk
Requires: baxcloud_core · Flutter 3.10+
Features
- Drop-in chat via
BaxChatView— joins a BaxCloud room and renders messaging UI - Ready-made modes: standard (full chat screen), compact (side panel), liveOverlay (stream comments)
- Deep theming: colors, radii, fonts, spacing, edge fade (messages dissolve at top/bottom)
- Join / leave presence lines — toggle on/off +
{name}templates or builders (i18n) - Typing indicators — off by default; opt in with
enableTypingIndicators+ BYOtypingBuilder - Hide input (
showInput: false) and send viaBaxChatController BaxChatsbootstrap +BaxChatControllerfor programmatic send / reactions- Persist messages yourself:
onMessage+initialMessages/seedMessages(BaxCloud does not store history) - BYO design:
messageBuilder,inputBuilder,typingBuilder,inChatBuilder - HTTP send via BaxCloud API + LiveKit data receive (server broadcast)
- Shared
BaxcloudUseridentity model (same as Calls / Meet / Live kits) - Automatic SDK telemetry on join (via
baxcloud_core)
Documentation
| Resource | Link |
|---|---|
| Chat UI Kit guide (modes, customization, i18n) | baxcloud.tech/docs/uikits/chat |
| UI Kits overview | baxcloud.tech/docs/uikits |
| API authentication | baxcloud.tech/docs/api/authentication |
| Dashboard | baxcloud.tech/dashboard |
Installation
dependencies:
baxcloud_core: ^0.1.5
baxcloud_chat_uikit_sdk: ^0.1.0
flutter pub get
Use a client key (bax_pk_…) with messaging scopes from the dashboard.
Rooms: Chat always requires a
roomName. Pre-create the room via REST or enable auto-create rooms on the project so the first join creates it.
No chat history: BaxCloud delivers messages in realtime only. It does not store chat history. If you need messages later, save them in your own database (see Message persistence).
Quick start
import 'package:baxcloud_core/baxcloud_core.dart';
import 'package:baxcloud_chat_uikit_sdk/baxcloud_chat_uikit_sdk.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await BaxChats.initialize(
config: BaxConfig(
projectId: 'your-project-id',
apiKey: 'bax_pk_your_client_key',
),
localUser: const BaxcloudUser(
userId: 'u1',
name: 'Ada',
avatarUrl: 'https://example.com/ada.png',
metadata: {'role': 'member', 'locale': 'en'},
),
);
runApp(const MyApp());
}
class ChatScreen extends StatelessWidget {
const ChatScreen({super.key});
@override
Widget build(BuildContext context) {
return BaxChatScope(
config: BaxConfig(
projectId: 'your-project-id',
apiKey: 'bax_pk_your_client_key',
),
child: BaxChatView(
roomName: 'support-lobby',
// user: optional — falls back to BaxChats.initialize localUser
uiConfig: BaxChatUiConfig.standard(),
),
);
}
}
Update local user
await BaxChats.updateLocalUser(
BaxcloudUser(
userId: 'u1',
name: 'Ada Lovelace',
metadata: {'locale': 'pt-BR'},
),
);
Hide input (controller-only send)
BaxChatView(
roomName: 'support-lobby',
uiConfig: BaxChatUiConfig.standard(showInput: false),
);
// From your chrome / toolbar:
await BaxChats.instance.activeController?.sendText('Hello from my UI');
Ready-made UI modes
Start from a preset and pass overrides as named args (no copyWith required):
| Preset | Use case |
|---|---|
BaxChatUiConfig.standard(...) |
Full chat screen — header, bubbles, input |
BaxChatUiConfig.compact(...) |
Side panel / embedded widget |
BaxChatUiConfig.liveOverlay(...) |
Live stream comments over video |
Full chat screen
BaxChatView(
roomName: 'team-general',
user: me,
uiConfig: BaxChatUiConfig.standard(
showTimestamps: true,
bubbleBorderRadius: 18,
localBubbleColor: const Color(0xFF2563EB),
),
);
Live streaming comments
Overlay the kit on your video. Messages fade at the top by default.
Stack(
fit: StackFit.expand,
children: [
MyLiveVideoPlayer(),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 280,
child: BaxChatView(
roomName: 'live-123',
user: me,
uiConfig: BaxChatUiConfig.liveOverlay(
joinedTextTemplate: '{name} joined the stream',
edgeFade: BaxChatEdgeFade.top,
edgeFadeExtent: 64,
),
),
),
),
],
);
Compact side panel
SizedBox(
width: 320,
child: BaxChatView(
roomName: 'support-42',
user: me,
uiConfig: BaxChatUiConfig.compact(),
),
);
Join / leave messages (i18n)
Control whether presence lines are generated and shown:
BaxChatUiConfig.standard(
showJoinMessages: true, // generate join events
showLeaveMessages: true, // generate leave events
showSystemMessages: true, // paint system bubbles
// Simple template — {name} is replaced:
joinedTextTemplate: '{name} entrou',
leftTextTemplate: '{name} saiu',
// Or full builder for complex i18n:
joinedTextBuilder: (name) => '$name joined the chat',
leftTextBuilder: (name) => '$name left the chat',
);
Typing indicators
Off by default for every preset. Opt in explicitly:
BaxChatUiConfig.standard(
enableTypingIndicators: true,
typingOneTemplate: '{name} is typing…',
typingTwoTemplate: '{name1} and {name2} are typing…',
typingManyTemplate: 'Several people are typing…',
// Or full i18n:
typingTextBuilder: (names) => '${names.join(', ')} typing…',
);
Bring-your-own typing UI (still requires enableTypingIndicators: true for built-in emit + slot):
BaxChatView(
roomName: room,
uiConfig: BaxChatUiConfig.standard(enableTypingIndicators: true),
typingBuilder: (context, users, controller) {
return Text(users.map((u) => u.userName).join(', ') + '…');
},
onTypingChanged: (users) => debugPrint('typing: $users'),
);
Or drive signals yourself without the built-in label (enableTypingIndicators: false):
await BaxChats.instance.activeController?.startTyping();
await BaxChats.instance.activeController?.stopTyping();
final who = BaxChats.instance.activeController?.typingUsers ?? [];
Customization
Three levels — same pattern as the Calls UI Kit.
1. Theme / layout (BaxChatUiConfig)
BaxChatUiConfig.standard(
backgroundColor: const Color(0xFF0B1220),
localBubbleColor: const Color(0xFF7C3AED),
remoteBubbleColor: const Color(0xFF374151),
bubbleBorderRadius: 20,
bubbleTailRadius: 4,
messageFontSize: 15,
showAvatars: true,
showReactionPicker: true,
reactionEmojis: ['👍', '❤️', '🔥'],
edgeFade: BaxChatEdgeFade.both,
edgeFadeExtent: 48,
inputHint: 'Type a message…',
emptyStateText: 'Say hello!',
);
Key options:
| Category | Fields |
|---|---|
| Visibility | showHeader, showSenderName, showAvatars, showTimestamps, showInput, showReactionPicker, showLeaveButton, showConnectionStatus |
| Presence | showJoinMessages, showLeaveMessages, showSystemMessages, joinedTextTemplate, leftTextTemplate, joinedTextBuilder, leftTextBuilder |
| Colors | backgroundColor, localBubbleColor, remoteBubbleColor, localTextColor, remoteTextColor, inputBackgroundColor, accentColor, … |
| Radii | bubbleBorderRadius, bubbleTailRadius, inputBorderRadius, systemMessageBorderRadius, avatarRadius |
| Edge fade | edgeFade (none / top / bottom / both), edgeFadeExtent |
| Copy (i18n) | inputHint, emptyStateText, connectedStatusText, connectingStatusText, leaveTooltip |
2. Partial chrome builders
BaxChatView(
roomName: room,
user: me,
messageBuilder: (context, message) {
return MyMessageBubble(message: message);
},
inputBuilder: (context, controller, onSend) {
return MyChatInput(onSend: onSend);
},
);
3. Full custom screen (inChatBuilder)
The kit manages the room session; you own the chrome.
BaxChatView(
roomName: room,
user: me,
inChatBuilder: (context, session) {
return Column(
children: [
MyAppBar(
title: session.roomName,
onLeave: session.leave,
),
Expanded(child: session.chatSurface),
],
);
},
);
Drive chat from anywhere while connected:
BaxChats.instance.activeController?.sendText('Hello from my toolbar!');
Controller
BaxChatController is bound while BaxChatView is connected.
final controller = BaxChats.instance.activeController;
// Send
await controller?.sendText('Hello!');
await controller?.sendReaction('👍');
// Read
final messages = controller?.messages ?? [];
final connected = controller?.isConnected ?? false;
// Persist / restore
controller?.onMessage = (msg) => myDb.insert(msg.toJson());
controller?.seedMessages(await myDb.loadForRoom('support-lobby'));
Message persistence
BaxCloud is a realtime transport for chat — messages are broadcast to connected participants and are not retained after the session. If your product needs history, store messages in your database.
Save every message
BaxChatView(
roomName: 'support-lobby',
user: me,
onMessage: (message) {
// Skip system lines if you only want user content:
if (message.type == BaxChatMessageType.system) return;
unawaited(myDb.upsertChatMessage(message.toJson()));
},
);
onMessage fires for local sends, remote receives, and (if enabled) join/leave system lines. Use message.isLocal, message.type, and message.toJson() as needed.
Restore history on join
Load from your DB, then pass as initialMessages (or call controller.seedMessages(...)). Seeded items are shown in the UI but do not re-trigger onMessage.
final history = await myDb.loadMessages(roomId: 'support-lobby');
BaxChatView(
roomName: 'support-lobby',
user: me,
initialMessages: history
.map((row) => BaxChatMessage.fromJson(row, isLocal: row['senderId'] == me.userId))
.toList(),
onMessage: (message) => myDb.upsertChatMessage(message.toJson()),
);
BaxChatView options
| Option | Description |
|---|---|
roomName |
BaxCloud room to join (required) |
user |
Optional BaxcloudUser — falls back to BaxChats.initialize localUser |
metadata |
Optional per-join map merged over user.metadata |
isHost |
Mark local participant as host (default false) |
canJoinWithNoHost |
Allow join when no host present (default true) |
config |
Override BaxConfig (else BaxChats / BaxChatScope) |
uiConfig |
Preset with named overrides (showInput, enableTypingIndicators, …); copyWith still available |
messageBuilder |
Custom message bubble |
inputBuilder |
Custom input bar |
typingBuilder |
Custom typing indicator UI |
inChatBuilder |
Replace entire screen chrome |
onMessage |
Persist each new message to your DB |
onTypingChanged |
Remote typing set changed |
initialMessages |
Prefill UI from your DB (no onMessage for these) |
onLeave |
Called when user leaves or disconnects |
loadingWidget / errorBuilder |
Join UX |
BaxChats.initialize(localUser:) sets the app-wide identity used for invites / default View identity. Pass user: on the View to override for that session (e.g. guest vs logged-in). If user is omitted, the kit uses localUser from initialize.
Example app
Bundled demos: Chat screen, Live streaming comments, Compact side panel, Bring your own chrome.
cd example
flutter run \
--dart-define=BAXCLOUD_PROJECT_ID=your_project_id \
--dart-define=BAXCLOUD_API_KEY=bax_pk_your_client_key
Build release APK:
cd example
flutter build apk --release \
--dart-define=BAXCLOUD_PROJECT_ID=your_project_id \
--dart-define=BAXCLOUD_API_KEY=bax_pk_your_client_key
Two-device test
- Device A → user id
user1, roomdemo-chat→ Save profile → Open demo - Device B → user id
user2, same room → Save profile → Open demo - Send messages on either device — they appear on both within the same room
Toggle Show join / leave messages on the home screen to test presence lines and i18n templates.
Support
License
See LICENSE.
Libraries
- baxcloud_chat_uikit_sdk
- BaxCloud Chat UI Kit — realtime messaging for Flutter apps.
- version