Matrix SDK

API reference for MeetingPlaceMatrixSDK, MeetingPlaceMatrixChatSDK, and MatrixConfig: the Matrix transport implementation for Meeting Place.

The Matrix SDK provides the Matrix transport implementation for the Meeting Place SDK. It extends MeetingPlaceCoreSDK to route individual and group chats over a Matrix homeserver rather than the default DIDComm transport. Use it as a drop-in replacement for MeetingPlaceCoreSDK wherever you need Matrix room features.

Use meeting_place_matrix when you need any of the following:

  • Individual or group Matrix chats with room history.
  • Native Matrix end-to-end encryption backed by vodozemac.
  • Rich messaging: image, video, file, and voice attachments, reactions, message editing and deletion, typing indicators, and delivery receipts.
  • Optional audio/video calling via a LiveKit SFU.

For applications that need only DIDComm-based messaging without Matrix room features, use meeting_place_chat directly.

Install Dependency

Package: meeting_place_matrix

dart pub add meeting_place_matrix

Check the latest version on the pub.dev registry or view the source on GitHub.

Requirements

  • Dart SDK ^3.8.0.
  • A running Matrix homeserver.
  • The vodozemac encryption runtime initialised before the first Matrix login. See Initialise the encryption runtime.
  • For audio/video calling: a LiveKit JWT service and a LiveKit SFU.

Initialise the encryption runtime

The Matrix SDK uses native end-to-end encryption backed by the vodozemac library. Initialise vodozemac once at application startup, before creating a MeetingPlaceMatrixSDK instance. Matrix client creation will fail if vodozemac has not been initialised first.

Flutter apps: add flutter_vodozemac to your pubspec.yaml and call init() from main():

import 'package:flutter/widgets.dart';
import 'package:flutter_vodozemac/flutter_vodozemac.dart' as fvod;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await fvod.init();
  // ... create MeetingPlaceMatrixSDK
}

Pure-Dart apps: build the vodozemac native library for your target platform (see the vodozemac README) and initialise it directly:

import 'package:vodozemac/vodozemac.dart' as vod;

Future<void> main() async {
  await vod.init(libraryPath: '/path/to/vodozemac/dylib/dir');
  // ... create MeetingPlaceMatrixSDK
}

Classes and Methods

MatrixConfig

MatrixConfig carries the configuration required to connect MeetingPlaceMatrixSDK to a Matrix homeserver. Pass an instance to MeetingPlaceMatrixSDK.create().

Constructor

mediatorDid String Required
DIDComm mediator DID used for discovery and connection flows.
controlPlaneDid String Required
Control Plane DID used for discovery and Matrix JWT login.
homeserver Uri Required
URL of the Matrix homeserver (for example, https://matrix.example.com).
databaseFactory MatrixDatabaseFactory Required
Opens the local Matrix database for sessions, sync state, and encryption data.
deviceId String Required
Device identifier used for Matrix device binding.
serverName String Optional
Matrix server name for user ID derivation (@hash:<serverName>). Defaults to homeserver.host. Set this explicitly when a tunnel or reverse proxy hostname differs from the Synapse server_name, to ensure all clients derive consistent user IDs.
livekitServiceUrl Uri? Optional
URL of the LiveKit JWT service for issuing call tokens. Required to enable audio/video calling. When omitted, the call plugin is not created.
livekitSfuUrl Uri? Optional
WebSocket URL of the LiveKit SFU. Required when enabling audio/video calling: MeetingPlaceMatrixSDK.create() throws MeetingPlaceLiveKitCallMisconfiguredException when this is omitted and livekitServiceUrl is set. Also overrides the SFU URL from the JWT token response, which is useful in local development when the container-internal hostname is not reachable from the device.
outgoingCallTimeout Duration Optional
How long the caller waits for the remote party to answer before the call ends and is reported as missed. Defaults to 60 seconds.

Example

import 'package:meeting_place_matrix/meeting_place_matrix.dart';

final config = MatrixConfig(
  mediatorDid: 'did:web:mediator.example.com:.well-known',
  controlPlaneDid: 'did:web:controlplane.example.com',
  homeserver: Uri.parse('https://matrix.example.com'),
  databaseFactory: matrixDatabaseFactory,
  deviceId: 'device-id-1',
);

MeetingPlaceMatrixSDK

MeetingPlaceMatrixSDK implements MeetingPlaceCoreSDK backed by a Matrix homeserver. It delegates all core behaviour to an inner MeetingPlaceCoreSDK instance and intercepts Matrix-specific transport calls. Use it anywhere a MeetingPlaceCoreSDK instance is accepted. The additional properties and methods on this class expose audio/video call management.

create

A static factory that creates an instance of MeetingPlaceMatrixSDK. Audio/video calling is enabled when livekitServiceUrl, rtcDelegate, and roomFactory are all provided.

Parameters

wallet Wallet Required

A digital wallet to manage cryptographic keys for signing and verification.

Requires the ssi package.

repositoryConfig RepositoryConfig Required
Storage configuration providing connection offer, group, channel, and key repositories.
config MatrixConfig Required
Matrix connection configuration. See MatrixConfig.
options MeetingPlaceMatrixSdkOptions Optional
SDK-level options such as retry behaviour, timeouts, and DID resolver address. Defaults apply when omitted.
logger MeetingPlaceCoreSDKLogger Optional
Optional logger for custom logging behaviour.
rtcDelegate WebRTCDelegate Optional
WebRTC delegate required for audio/video calling. Provide together with roomFactory and MatrixConfig.livekitServiceUrl. Flutter apps should use FlutterMatrixRTCDelegate from meeting_place_livekit_flutter.
roomFactory LiveKitRoomFactory Optional
LiveKit room factory required for audio/video calling. Provide together with rtcDelegate and MatrixConfig.livekitServiceUrl. Flutter apps should use FlutterLiveKitRoom from meeting_place_livekit_flutter.

Example

import 'package:meeting_place_matrix/meeting_place_matrix.dart';
import 'package:ssi/ssi.dart';

final matrixSDK = await MeetingPlaceMatrixSDK.create(
  wallet: Bip32Wallet.fromSeed(seed),
  repositoryConfig: RepositoryConfig(
    connectionOfferRepository: ConnectionOfferRepositoryImpl(storage: storage),
    groupRepository: GroupRepositoryImpl(storage: storage),
    channelRepository: ChannelRepositoryImpl(storage: storage),
    keyRepository: KeyRepositoryImpl(storage: storage),
  ),
  config: MatrixConfig(
    mediatorDid: 'did:web:mediator.example.com:.well-known',
    controlPlaneDid: 'did:web:controlplane.example.com',
    homeserver: Uri.parse('https://matrix.example.com'),
    databaseFactory: matrixDatabaseFactory,
    deviceId: 'device-id-1',
  ),
);

Properties

isCallSupported bool Optional
Whether audio/video calling is available. Returns false when livekitServiceUrl was not set, or rtcDelegate and roomFactory were not passed to create().
incomingCalls Stream<IncomingAudioVideoCallEvent> Optional
Stream of incoming call events. Empty when no call plugin is configured.
cancelledCalls Stream<IncomingAudioVideoCallEvent> Optional
Stream of cancelled incoming-call events. Empty when no call plugin is configured.
activeCallSession LiveKitCallSession? Optional
The currently active call session, or null when no call is in progress.

startCall

Starts an outbound audio or video call to another participant. Returns a live AudioVideoCallSession handle.

Parameters

otherPartyChannelDid String Required
The channel DID of the participant to call.
mediaType CallMediaType Required
The type of call: CallMediaType.audio or CallMediaType.audioVideo.

Throws MeetingPlaceLiveKitCallOperationException when no call plugin is configured. Check isCallSupported before calling.

Example

if (matrixSDK.isCallSupported) {
  final session = await matrixSDK.startCall(
    otherPartyChannelDid: 'did:example:bob-channel',
    mediaType: CallMediaType.audioVideo,
  );
}

acceptCall

Accepts an incoming call.

Parameters

callId String Required
The ID of the incoming call to accept.

Throws MeetingPlaceLiveKitCallOperationException when no call plugin is configured.

Example

matrixSDK.incomingCalls.listen((event) async {
  await matrixSDK.acceptCall(callId: event.callId);
});

declineCall

Declines an incoming call.

Parameters

callId String Required
The ID of the incoming call to decline.

Throws MeetingPlaceLiveKitCallOperationException when no call plugin is configured.

Example

matrixSDK.incomingCalls.listen((event) async {
  await matrixSDK.declineCall(callId: event.callId);
});

leaveCurrentCall

Leaves the currently active call. No-op when no call is in progress or no call plugin is configured.

Parameters

No parameters required.

Example

await matrixSDK.leaveCurrentCall();

ringGroupMember

Sends a targeted call-invite notification to a single group member, leaving other members undisturbed.

Parameters

groupChannelDid String Required
The channel DID of the group.
memberDid String Required
The DID of the group member to ring.
mediaType CallMediaType Required
The type of call: CallMediaType.audio or CallMediaType.audioVideo.

Throws MeetingPlaceLiveKitCallOperationException when no call plugin is configured.

Example

await matrixSDK.ringGroupMember(
  groupChannelDid: 'did:example:group-channel',
  memberDid: 'did:example:member-alice',
  mediaType: CallMediaType.audio,
);

MeetingPlaceMatrixChatSDK

MeetingPlaceMatrixChatSDK is the Matrix-backed chat SDK for individual and group conversations. It implements MeetingPlaceChatSDK and provides rich messaging features: attachments, reactions, message editing and deletion, typing indicators, and delivery receipts.

This class only supports channels with transport == ChannelTransport.matrix. For DIDComm channels, use ChatSDK.initialiseFromChannel() from meeting_place_chat.

initialiseFromChannel

A static factory that creates a MeetingPlaceChatSDK instance from a Matrix channel. Returns a group or individual Matrix chat implementation based on the channel type.

Parameters

channel Channel Required
The channel to create the chat from. Must have transport == ChannelTransport.matrix.
coreSDK MeetingPlaceCoreSDK Required
An instance of MeetingPlaceMatrixSDK or any MeetingPlaceCoreSDK backed by the Matrix transport.
chatRepository ChatRepository Required
The repository used for persisting chat messages and sync state.
options MeetingPlaceChatSDKOptions Required
Chat session configuration, including activity expiry, delete window, and presence intervals.
card ContactCard Optional
Optional contact card representing the local user’s profile.
logger MeetingPlaceMatrixSDKLogger Optional
Optional logger for custom logging behaviour.

Example

import 'package:meeting_place_matrix/meeting_place_matrix.dart';

final chatSDK = await MeetingPlaceMatrixChatSDK.initialiseFromChannel(
  channel,
  coreSDK: matrixSDK,
  chatRepository: chatRepository,
  options: const MeetingPlaceMatrixChatSdkOptions(),
  card: myContactCard,
);

startChatSession

Starts a Matrix chat session. Subscribes to the Matrix room, loads persisted messages, and fetches missed room history since the last session marker. Returns a Chat object immediately with the locally persisted snapshot. Matrix auth and history backfill run in the background and push updates through chat.stream.

Parameters

No parameters required.

Example

final chat = await chatSDK.startChatSession();

// Render the initial snapshot
for (final message in chat.messages) {
  print(message);
}

// Listen for new messages and events
chat.stream.listen((data) {
  print('New event: ${data.chatItem}');
});

sendTextMessage

Sends a text message, optionally with attachments.

Parameters

text String Required
The message text to send.
attachments List<ChatAttachment> Optional
Optional list of attachments (image, video, file, or voice). Defaults to an empty list.

Example

// Plain text
await chatSDK.sendTextMessage('Hello via Matrix!');

// With an image attachment
await chatSDK.sendTextMessage(
  'Check this out',
  attachments: [
    ChatAttachment(
      data: imageBytes,
      contentType: 'image/jpeg',
      filename: 'photo.jpg',
    ),
  ],
);

reactOnMessage

Toggles a reaction on a message. Adds the reaction if the local user has not already reacted with that emoji. Removes it if they have.

Parameters

message Message Required
The message to react to.
reaction String Required
The emoji to add or remove as a reaction.

Example

await chatSDK.reactOnMessage(message, reaction: '👍');

editTextMessage

Edits the text of a previously sent message. Only the original sender can edit. The message must have been delivered to the server (has a transportId).

Parameters

message Message Required
The message to edit. Must be sent by the local user.
newText String Required
The replacement text. Must be non-empty and different from the current text.

Example

await chatSDK.editTextMessage(message, 'Corrected message text');

deleteMessage

Deletes a previously sent message. Only the original sender can delete. The deletion window is configured via MeetingPlaceChatSDKOptions.deleteMessageWindow.

Parameters

message Message Required
The message to delete. Must be sent by the local user.
localOnly bool Optional
When true, hides the message locally only without sending a Matrix redaction event to the room. Defaults to false.

Example

// Delete for all participants
await chatSDK.deleteMessage(message);

// Hide locally only
await chatSDK.deleteMessage(message, localOnly: true);

sendChatActivity

Sends a typing indicator to the Matrix room. The indicator clears automatically after MeetingPlaceChatSDKOptions.chatActivityExpiry elapses without a new call.

Parameters

No parameters required.

Example

// Call when the local user starts typing
await chatSDK.sendChatActivity();

sendChatDeliveredMessage

Sends an m.read receipt to the Matrix room, marking a message as delivered.

Parameters

messageId String Required
The Matrix event ID of the message to mark as delivered.

Example

await chatSDK.sendChatDeliveredMessage(message.transportId!);

downloadMedia

Downloads a media attachment hosted on the Matrix homeserver. The attachment must have a non-null transportId.

Parameters

attachment ChatAttachment Required
The attachment to download.

Example

final bytes = await chatSDK.downloadMedia(attachment);

sendCustomEvent

Dispatches an arbitrary Matrix room event. The SDK does not persist a ChatItem or push to chatStream for the sender. Receivers handle the event through their incoming routers based on the event type. Use this as a low-level escape hatch for event types not supported by the built-in methods.

Parameters

type String Required
The Matrix event type string (for example, com.example.my_event).
payload Map<String, dynamic> Required
The event content payload.

Example

await chatSDK.sendCustomEvent(
  type: 'com.example.read_status',
  payload: {'status': 'online'},
);

end

Ends the current chat session, cancels the Matrix room subscription, and releases resources.

Parameters

No parameters required.

Example

await chatSDK.end();

Flutter call rendering

Flutter applications require the companion LiveKit Flutter plugin to render audio and video call tracks. The plugin provides FlutterMatrixRTCDelegate and FlutterLiveKitRoom: the concrete implementations of the rtcDelegate and roomFactory parameters accepted by MeetingPlaceMatrixSDK.create().

Samples

Initialise the SDK and publish a Matrix offer

The following example initialises vodozemac, creates the MeetingPlaceMatrixSDK, and publishes a connection offer using the Matrix transport.

import 'package:meeting_place_core/meeting_place_core.dart';
import 'package:meeting_place_matrix/meeting_place_matrix.dart';
import 'package:ssi/ssi.dart';
import 'package:vodozemac/vodozemac.dart' as vod;

Future<void> main() async {
  // 1. Initialise the vodozemac encryption runtime before any Matrix login.
  await vod.init(libraryPath: '/path/to/libvodozemac');

  // 2. Create the Matrix SDK.
  final matrixSDK = await MeetingPlaceMatrixSDK.create(
    wallet: Bip32Wallet.fromSeed(seed),
    repositoryConfig: RepositoryConfig(
      connectionOfferRepository: ConnectionOfferRepositoryImpl(storage: storage),
      groupRepository: GroupRepositoryImpl(storage: storage),
      channelRepository: ChannelRepositoryImpl(storage: storage),
      keyRepository: KeyRepositoryImpl(storage: storage),
    ),
    config: MatrixConfig(
      mediatorDid: 'did:web:mediator.example.com:.well-known',
      controlPlaneDid: 'did:web:controlplane.example.com',
      homeserver: Uri.parse('https://matrix.example.com'),
      databaseFactory: matrixDatabaseFactory,
      deviceId: 'device-id-1',
    ),
  );

  // 3. Register for DIDComm notifications.
  await matrixSDK.registerForDIDCommNotifications();

  // 4. Publish a connection offer with Matrix transport.
  final result = await matrixSDK.publishOffer(
    offerName: 'My Matrix offer',
    offerDescription: 'Connect with me over Matrix.',
    contactCard: ContactCard(
      did: 'did:example:alice',
      type: 'individual',
      contactInfo: const {},
    ),
    type: SDKConnectionOfferType.invitation,
    validUntil: DateTime.now().toUtc().add(const Duration(hours: 24)),
    transport: ChannelTransport.matrix,
  );

  print('Share this mnemonic to connect: ${result.connectionOffer.mnemonic}');
}

Start a Matrix chat session

Once a channel has been established with ChannelTransport.matrix, use MeetingPlaceMatrixChatSDK to open a chat session, send messages, and listen for incoming events.

import 'package:meeting_place_matrix/meeting_place_matrix.dart';

// matrixSDK and channel (ChannelTransport.matrix) must already be created.
final chatSDK = await MeetingPlaceMatrixChatSDK.initialiseFromChannel(
  channel,
  coreSDK: matrixSDK,
  chatRepository: chatRepository,
  options: const MeetingPlaceMatrixChatSdkOptions(),
);

final chat = await chatSDK.startChatSession();

// Render the initial persisted snapshot
for (final message in chat.messages) {
  print(message);
}

// Listen for incoming messages and events
chat.stream.listen((data) {
  print('Event: ${data.chatItem}');
});

// Send a plain text message
await chatSDK.sendTextMessage('Hello via Matrix!');

// React to a received message
await chatSDK.reactOnMessage(message, reaction: '👍');

// End the session and release resources when done
await chatSDK.end();

Enable audio/video calling with LiveKit

Pass LiveKit configuration to MatrixConfig and provide rtcDelegate and roomFactory to MeetingPlaceMatrixSDK.create(). The call plugin is only created when all three are present. For Flutter apps, use FlutterMatrixRTCDelegate and FlutterLiveKitRoom from meeting_place_livekit_flutter.

import 'package:meeting_place_matrix/meeting_place_matrix.dart';
import 'package:ssi/ssi.dart';

final matrixSDK = await MeetingPlaceMatrixSDK.create(
  wallet: wallet,
  repositoryConfig: repositoryConfig,
  config: MatrixConfig(
    mediatorDid: 'did:web:mediator.example.com:.well-known',
    controlPlaneDid: 'did:web:controlplane.example.com',
    homeserver: Uri.parse('https://matrix.example.com'),
    databaseFactory: matrixDatabaseFactory,
    deviceId: 'device-id-1',
    livekitServiceUrl: Uri.parse('https://livekit-jwt.example.com'),
    livekitSfuUrl: Uri.parse('wss://livekit.example.com'),
  ),
  rtcDelegate: webRtcDelegate,
  roomFactory: liveKitRoomFactory,
);

// Listen for incoming calls
matrixSDK.incomingCalls.listen((event) async {
  print('Incoming call: ${event.callId}');
  await matrixSDK.acceptCall(callId: event.callId);
});

// Start an outbound audio/video call
if (matrixSDK.isCallSupported) {
  final session = await matrixSDK.startCall(
    otherPartyChannelDid: 'did:example:bob-channel',
    mediaType: CallMediaType.audioVideo,
  );
}

// Ring a single group member for a group call
await matrixSDK.ringGroupMember(
  groupChannelDid: 'did:example:group-channel',
  memberDid: 'did:example:member-alice',
  mediaType: CallMediaType.audio,
);

// Leave the active call
await matrixSDK.leaveCurrentCall();