Skip to content

Flutter SDK

Headless client for your own UI. Prefer widgets? Use the Flutter UI Kit.

Before code: mint a session token on your server. Never ship sk_* in the app.
Guided path: Quick start → Flutter SDK.

Fast path

Do this in order — most integrations are these five steps.

1. Install

bash
flutter pub add sendsar_chat

2. Connect

dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:sendsar_chat/sendsar_chat.dart';

Future<SessionResponse> fetchChatSession() async {
  final res = await http.post(
    Uri.parse('https://your-app.com/api/chat/session'),
    headers: {'Cookie': '…'},
  );
  if (res.statusCode < 200 || res.statusCode >= 300) {
    throw Exception(res.body);
  }
  return SessionResponse.fromJson(jsonDecode(res.body) as Map<String, dynamic>);
}

final session = await fetchChatSession();
final client = Sendsar.init(SendsarInitOptions(apiUrl: session.apiUrl));
await client.connect(ConnectOptions(
  userId: session.chatUserId,
  token: session.token,
));

3. Conversation list (inbox)

dart
final rooms = await client.listRooms();
setConversations(rooms); // ConversationListPage

client.on<Message>(SocketEvent.newMessage, (msg) {
  bumpConversation(msg); // preview, time, sort, unread
});

4. Message thread (open room)

Create in ChatPage; destroy in dispose.

dart
final sub = createRoomSubscription(
  client,
  RoomSubscriptionOptions(
    roomId: openRoomId,
    userId: session.chatUserId,
    onInitialMessages: (messages, peerLastReadAt, [nextCursor]) {
      setMessages(messages);
    },
    onMessage: (msg) {
      appendById(msg);
      bumpConversation(msg);
    },
    onMessageUpdated: (msg) => replaceById(msg),
  ),
);

// dispose:
sub.destroy();

5. Send

dart
final sent = await client.sendMessage(openRoomId, SendMessageParams(
  parts: [MessagePart(type: 'text', text: 'Hello')],
));
appendById(sent);
bumpConversation(sent);

Screens (what to update)

ScreenShowsYour widget
Conversation listRooms, last message, unreadConversationListPage
Message threadBubbles + composer for one roomIdChatPage / MessageList

Also common: Room info, Call UI.

HappensConversation listMessage thread
createRoomAdd rowOptional: open it
New messageBump preview / unreadAppend if that room is open
Edit / delete / reaction / pinPreview if latestReplace by id
forwardMessageBump targetsAppend in open target
deleteConversationRemove rowPop if open
clearHistoryClear previewEmpty list
Members changeOptional subtitleRoom info
Incoming callOptional badgeCall UI

WARNING

messageUpdatedreplace by id. Never append a second bubble.


Messaging APIs

Create room & send text

dart
final room = await client.createRoom(CreateRoomParams(
  externalId: 'dm:${session.chatUserId}:user_bob',
  participants: [
    CreateRoomParticipant(id: session.chatUserId),
    CreateRoomParticipant(id: 'user_bob'),
  ],
));
// → Conversation list: add row

final sent = await client.sendMessage(room.id, SendMessageParams(
  parts: [MessagePart(type: 'text', text: 'Hello')],
));
// → Thread: append · List: bump · Event: newMessage

Edit, delete, reply

dart
await client.updateMessage(roomId, messageId, UpdateMessageParams(
  parts: [MessagePart(type: 'text', text: 'Updated')],
)); // → replace · messageUpdated

await client.deleteMessage(roomId, messageId);
// → replace (deletedAt)

await client.sendMessage(roomId, SendMessageParams(
  parts: [MessagePart(type: 'text', text: 'Replying!')],
  parentMessageId: messageId,
)); // → append · newMessage

Reactions

dart
await client.toggleReaction(
  roomId,
  messageId,
  ToggleReactionParams(emoji: '👍'),
); // → replace (reactions) · messageUpdated

Pin & forward

dart
await client.pinMessage(roomId, messageId);
await client.unpinMessage(roomId, messageId);

await client.forwardMessage(roomId, messageId, ForwardMessageParams(
  targetRoomIds: [otherRoomId],
)); // → target rooms: newMessage + bump list

Send files

dart
final message = await client.sendFileMessage(
  roomId,
  bytes: fileBytes,
  filename: 'photo.jpg',
  mediaType: 'image/jpeg',
); // → append · hydrate URLs for previews

Members & conversation

dart
await client.addParticipant(roomId, AddParticipantParams(userId: otherUserId));
await client.removeParticipant(roomId, otherUserId);
client.on(SocketEvent.roomParticipantsChanged, (ev) {
  if (ev.roomId == roomId) refreshMembers();
});

await client.deleteConversation(roomId);
await client.clearHistory(roomId);

Voice & video (optional)

Enable in the dashboard, then flutter pub add sendsar_call (+ mic/camera permissions).

dart
import 'package:sendsar_call/sendsar_call.dart';

final calls = CallClient(CallClientOptions(chat: client));
calls.on('incoming', (invite) => showIncomingCall(invite));
calls.on('localTrack', (e) { /* bind */ });
calls.on('remoteTrack', (e) { /* bind */ });
await calls.start(room.id, const CallStartOptions(type: 'video'));

Checklist

  • [ ] Session from your BFF — no sk_* in the app
  • [ ] Conversation list: listRooms + newMessage bumps
  • [ ] Message thread: createRoomSubscription (destroy on leave)
  • [ ] messageUpdated → replace by id
  • [ ] Files: hydrate access URLs for previews
  • [ ] Calls (if needed): sendsar_call + dashboard flag

Next