Standalone RTC channels
Standalone channels are for meetings, voice rooms and similar scenarios that don't depend on an IM group: users join with permission from your backend, with no ringing or incoming-call UI. In Flutter, join and leave with im.channels; audio and video come from the deeprespond_im_call package (LiveKit), on Android and iOS. Native iOS apps don't have a separate Swift SDK yet, so integrate through Flutter. For creating channels and granting access, see Channels overview in the server docs.
| You want to | See |
|---|---|
| Add the packages, request permissions | Dependencies and permissions |
| Join, leave, show state and video | Join and leave |
| Restore after the app restarts | API and restore |
| Video widgets, behavior on sign-out and in the background | Video widgets and lifecycle |
Dependencies and permissions
- Use matching versions of
deeprespond_im,deeprespond_im_flutteranddeeprespond_im_callthat include the channel API.deeprespond_im_callprovides audio and video automatically when the client is created; you don't need to setDRClientOptions.call. - Create the client
imand sign in first, as in Initialization. - On iOS, add
NSMicrophoneUsageDescriptionandNSCameraUsageDescriptiontoInfo.plist; on Android, declare and request the microphone and camera permissions. - Joining doesn't turn on the microphone or camera by default; if the user denies permissions, they can still just watch and listen.
- Viewer-only participants (
subscriber) can't publish, and audio-only channels (audio) can't turn on the camera.
Join and leave
dart
import 'package:deeprespond_im_flutter/deeprespond_im_flutter.dart';
import 'package:deeprespond_im_call/deeprespond_im_call.dart';
if (!im.channels.supported) throw StateError('当前环境没有频道媒体适配');
final cancel = DRCancelToken();
final channel = await im.channels.join('meeting-demo',
options: ChannelJoinOptions(ticket: ticketFromBusinessServer, cancel: cancel),
);
// join 成功表示媒体连接与 HTTP joined 确认完成。
await channel.setMicrophoneEnabled(true); // 在用户点击后调用。
await channel.setCameraEnabled(true); // 仅 video publisher。
final states = channel.stream.listen((snapshot) {
print(snapshot.session.status);
});
final tracks = channel.tracks.listen((items) {
// 完整的已授权轨道列表;sessionId 保持 String。
});
final localVideo = channel.localView();
// 实际远端 ID 来自授权 ChannelTrack.sessionId:
final remoteVideo = channel.remoteView(remoteSessionId);
await states.cancel();
await tracks.cancel();
await channel.leave();ticketFromBusinessServercomes from the join permission your backend returns, andremoteSessionIdfrom a track; never log the permission. Open channels don't need aticket.- Cancel a
jointhat hasn't finished withcancel.cancel(); once you have the handle, leave withleave(). - When joining fails or a connection arrives late, the SDK cleans up audio and video; even if the
leaverequest fails, the local microphone and camera stop.
API and restore
| API | Description |
|---|---|
supported, current | Whether this device has audio/video capability; this device's current channel handle |
getChannel(channelId) | Gets a channel |
getCurrentSession() | This sign-in's channel session, or null if none |
listParticipants(channelId, cursor:, limit:) | Current members; requires being in the channel yourself |
join(channelId, options:) | Joins, without turning on the microphone or camera by default |
resume(channelId:, sessionId:, cancel:) | Restores the original session without taking a new slot |
dart
final existing = await im.channels.getCurrentSession();
if (existing != null && ['joining', 'joined'].contains(existing.status)) {
final restored = await im.channels.resume(
channelId: existing.channelId, sessionId: existing.sessionId,
);
// 用户确认后才重新开启采集。
}- A channel handle has
channelId,sessionId,snapshot,stream,tracks,currentTracks,refresh(),setMicrophoneEnabled(),setCameraEnabled()andleave(). - Subscribing to
streamandtracksfirst delivers the current value. ChannelSnapshothaschannel,session,members,mediaStateanderror.- When this device's current channel changes, the client emits a
ChannelChangedevent.
Video widgets and lifecycle
ChannelVideoView(channel:, local:, sessionId:, fit:, placeholder:)shows local video or a given session's video; the handle also haslocalView(),remoteView(sessionId),switchCamera()andsetSpeakerphoneOn().- The SDK only provides tracks confirmed to belong to members allowed to join.
- Removing a video widget only stops displaying it; still call
leave()when leaving the screen. - The SDK sends heartbeats and renews the join permission automatically, and handles sign-out, sign-in switches, rejection and session expiry. After a different IM sign-in, the original session can't be restored.
- The server checks permission on every join; the media connection's own token refresh doesn't mean permission lasts forever.
- Channels don't use CallKit or the system incoming-call UI, and there's no incoming-call notification.
- Capturing audio or video continuously in the background needs extra work and testing, such as a foreground service and iOS background audio.
Channels are currently available as source only: no release including them has been published yet, and multi-party calls haven't been tested on real devices.
