BaxCloud Flutter SDK

A comprehensive Flutter SDK for Live Streaming, Calls, and Signaling. Provides ready-to-use UI kits to speed up implementation.

Features

  • Live Streaming UI Kit - Complete UI for live streaming with controls
    • Viewer mode - Join as viewer without publishing
    • Co-host management - Invite and manage co-hosts via signaling
    • Draggable participant windows - Drag and position co-host windows with corner snapping
    • Viewers counter widget - Standalone, customizable viewers counter with management
    • Viewers list - Get list of viewers and publishers
    • Participant management - Mute, disable video, remove participants, and invite to co-host (host/co-host only)
  • Calls UI Kit - 1-to-1 and group video/voice calls
    • 1-to-1 Video Call
    • 1-to-1 Voice Call
    • Group Video Call
    • Group Voice Call
  • Signaling / Chat UI Kit - Messages, reactions, customizable UI
    • TikTok-style live chat widget - Vertical message list with animations
    • Real-time messaging - Direct data channel communication
    • System messages - Join/leave notifications
    • Customizable styling - Complete UI control
  • PK (Player Kill) Battle UI Kit - Split-screen battle interface with voting
    • PK battle invitations - Invite others from live streaming sessions
    • Invitation system - Send, receive, accept, and decline invitations
    • Auto-start battle - Automatically transition to battle when accepted
  • Advanced Features - Screen sharing, network quality indicators, live stats
  • Participant Management - Mute/unmute participants with server-side authorization
  • Session Management - List, view analytics, and terminate active sessions
  • Analytics & Statistics - Get detailed room analytics and participant statistics
  • 🎯 Easy Integration - Simple 3-step API
    • Quick initialization: BaxCloudQuickStart.init()
    • Auto-initialized controllers: BaxCloudQuickStart.createLiveStreamingController()
    • Ready-to-use UI kits
  • 🎨 Customizable UI - Built-in UI components with customization options
  • 🔔 Customizable Messages - Customize or disable ScaffoldMessenger messages
  • 🔐 Secure - Server-side token generation support

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  baxcloud_client: ^0.0.1

Then run:

flutter pub get

Professional SDK

BaxCloud SDK is a professional SDK where the server URL defaults to live.baxcloud.com. You don't need to configure or provide server URLs - the SDK handles this for you.

Note: For local development or custom server setups, you can provide an optional customServerUrl parameter to override the default server URL.

Custom Server Setup (Optional)

If you need to run your own server for token generation and configuration:

  1. Install Server Dependencies:
cd server
npm install
  1. Configure Environment:
cp .env.example .env

Edit .env:

SERVER_URL=wss://your-server-url
API_KEY=your_api_key
API_SECRET=your_api_secret
PORT=3000
  1. Start Server:
npm start
  1. Use Custom Server in SDK:
BaxCloudQuickStart.init(
  appId: 'your-app-id',
  apiKey: 'your-api-key',
  customServerUrl: 'http://localhost:3000', // Your custom server
);

See server/README.md for detailed server documentation.

Getting Started

Quick Start

For the simplest integration with server-side token generation:

import 'package:baxcloud_client/baxcloud_client.dart';

// 1. Initialize SDK - server URL defaults to live.baxcloud.com
BaxCloudQuickStart.init(
  appId: 'your-app-id', // Required for SaaS identification
  apiKey: 'your-api-key', // Required for SaaS identification
  // customServerUrl: 'https://your-custom-server.com', // Optional: override default
);

// 2. Create controller (auto-initialized)
final controller = BaxCloudQuickStart.createLiveStreamingController();

// 3. Use the UI kit - serverUrl and token are fetched automatically!
BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  userId: 'user-123',
  username: 'John Doe',
)

Full Initialization

For more control, use the full initialization:

import 'package:baxcloud_client/baxcloud_client.dart';

// Initialize SDK
// Professional SDK - server URL defaults to live.baxcloud.com
// serverUrl and tokens are automatically fetched from server
final config = BaxCloudConfig(
  appId: 'your-app-id', // Required for SaaS identification
  apiKey: 'your-api-key', // Required for SaaS identification
  // customServerUrl: 'https://your-custom-server.com', // Optional: override default
  userId: 'user123', // Optional default user ID
  username: 'John Doe', // Optional default username
  avatarUrl: 'https://example.com/avatar.jpg', // Optional avatar URL
);

BaxCloudClient.instance.init(config);

Quick Start with User Info

// Initialize with user info in one step
// Professional SDK - server URL defaults to live.baxcloud.com
BaxCloudQuickStart.initWithUser(
  appId: 'your-app-id', // Required for SaaS identification
  apiKey: 'your-api-key', // Required for SaaS identification
  // customServerUrl: 'https://your-custom-server.com', // Optional: override default
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
);

2. Live Streaming

Simplest Usage (3 Steps)

// Step 1: Initialize (one-time, in your app startup)
// Professional SDK - server URL defaults to live.baxcloud.com
BaxCloudQuickStart.init(
  appId: 'your-app-id', // Required for SaaS identification
  apiKey: 'your-api-key', // Required for SaaS identification
  // customServerUrl: 'https://your-custom-server.com', // Optional: override default
);

// Step 2: Create controller (auto-initialized)
final controller = BaxCloudQuickStart.createLiveStreamingController();

// Step 3: Use the UI kit - that's it!
BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  onConnected: () => print('Connected!'),
)

Basic Usage

// Create controller
final controller = LiveStreamingController();
controller.initialize(BaxCloudClient.instance);

// Use the UI kit
// serverUrl is automatically fetched from server - no need to pass it!
BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  enableVideo: true,
  enableAudio: true,
  viewerMode: false, // Set to true to join as viewer
  onConnected: () {
    print('Connected to room');
  },
  onDisconnected: () {
    print('Disconnected from room');
  },
  onError: (error) {
    print('Error: $error');
  },
)

Advanced Usage with Controller

// Create and initialize controller
final controller = BaxcloudLiveStreamingController();
controller.initialize(BaxCloudClient.instance);

// Connect and start streaming
await controller.connectAndStart(
  roomName: 'my-room',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  enableVideo: true,
  enableAudio: true,
);

// Control streaming
controller.toggleVideo(); // Toggle video on/off
controller.toggleAudio(); // Toggle audio on/off
controller.switchCamera(); // Switch between front/back camera

// Disconnect
await controller.disconnect();

Viewer Mode

Join a live stream as a viewer (without publishing video/audio):

// Connect as viewer
await controller.connectAndStart(
  roomName: 'my-room',
  userId: 'user123',
  username: 'John Doe',
  viewerMode: true, // Connect as viewer
);

// Get list of viewers
final viewers = controller.viewers; // Participants not publishing
final publishers = controller.publishers; // Participants publishing

// Display viewers count
print('Viewers: ${viewers.length}');
print('Publishers: ${publishers.length}');

Co-host Management with Invitations

The co-host invitation system provides a comprehensive way to manage co-hosts with support for both audience requests and host invitations, similar to ZegoCloud's implementation.

Access the Co-host Controller:

final controller = BaxcloudLiveStreamingSeatsController();
controller.initialize(BaxCloudClient.instance);

// Access co-host controller
final coHostController = controller.coHost;

For Audience - Request to Become Co-host:

// Audience requests to become a co-host
await coHostController.audienceSendCoHostRequest(withToast: true);

// Cancel the request
await coHostController.audienceCancelCoHostRequest();

// Become co-host directly without request (if allowed)
await coHostController.startCoHost();

// Accept host's invitation
await coHostController.audienceAgreeCoHostInvitation(withToast: true);

// Reject host's invitation
await coHostController.audienceRejectCoHostInvitation();

For Host - Manage Co-host Requests and Invitations:

// Approve audience's co-host request
final audience = BaxcloudUIKITUser(id: 'user123', name: 'John');
await coHostController.hostAgreeCoHostRequest(audience);

// Reject audience's co-host request
await coHostController.hostRejectCoHostRequest(audience);

// Invite audience to become co-host
await coHostController.hostSendCoHostInvitationToAudience(audience, withToast: true);

// Remove a co-host
final coHost = BaxcloudUIKITUser(id: 'cohost123', name: 'Jane');
await coHostController.removeCoHost(coHost);

For Co-host - Stop Co-hosting:

// Co-host ends connection and switches to audience
await coHostController.stopCoHost(showRequestDialog: true);

Event Listeners:

// Listen to co-host updates
coHostController.events.onUpdated = (coHosts) {
  print('Co-hosts updated: ${coHosts.length}');
};

// Listen to max count reached
coHostController.events.onMaxCountReached = (count) {
  print('Maximum co-hosts limit reached: $count');
};

// Host events
coHostController.events.host.onRequestReceived = (audience) {
  print('Co-host request received from: ${audience.name}');
};

coHostController.events.host.onInvitationAccepted = (audience) {
  print('Invitation accepted by: ${audience.name}');
};

// Audience events
coHostController.events.audience.onRequestAccepted = () {
  print('Your co-host request was accepted!');
};

coHostController.events.audience.onInvitationReceived = (host) {
  print('Received co-host invitation from: ${host.name}');
};

Get Current Co-hosts:

final coHosts = coHostController.coHosts;
print('Current co-hosts: ${coHosts.length}');

Legacy Method (Still Supported):

// As host, invite a viewer to become co-host (uses new system internally)
await controller.inviteCoHost('viewer123');

Participant Management (Host/Co-host Only)

As a host or co-host, you can manage participants in the viewers and publishers lists. The participants list UI is automatically available in the live streaming views - just click the participants button (👥) in the host controls.

Available Actions:

  • Mute/Unmute - Mute or unmute a participant's audio
  • Disable Video - Disable a participant's video (if they have video enabled)
  • Remove - Remove a participant from the room
  • Invite to Co-host - Invite a participant to become a co-host
// Mute a participant
await controller.muteParticipant('participant123');

// Disable video for a participant
await controller.disableParticipantVideo('participant123');

// Remove a participant from the room
await controller.removeParticipant('participant123');

// Invite a participant to become co-host
await controller.inviteCoHost('participant123');

Note: These actions are only available to hosts and co-hosts. When you click on a participant in the list, a bottom sheet will appear with all available actions.

Draggable Participant Windows

The SDK provides draggable participant windows for co-hosts and publishers, allowing hosts to position video windows anywhere on screen with automatic corner snapping.

Basic Usage:

// In your main video area (Stack)
BaxcloudDraggableParticipantsOverlay(
  controller: liveController,
  localVideoTrack: localTrack,
  config: DraggableParticipantWindowConfig(
    defaultMargin: 10.0,
    cornerSnapThreshold: 50.0,
    initialCorner: 1, // 0: top-left, 1: top-right, 2: bottom-left, 3: bottom-right
    enableCornerSnapping: true,
    respectSafeArea: true,
  ),
  windowWidth: Get.width / 3.5,
  windowHeight: Get.width / 2.2,
  showLocalVideo: true,
  maxWindows: 3,
)

Configuration Options:

  • defaultMargin - Margin from screen edges (default: 10.0)
  • cornerSnapThreshold - Distance threshold for snapping to corners (default: 50.0)
  • initialCorner - Starting corner position (0-3: top-left, top-right, bottom-left, bottom-right)
  • enableCornerSnapping - Enable/disable corner snapping (default: true)
  • respectSafeArea - Whether to respect safe area insets (default: true)

Features:

  • ✅ Drag windows anywhere on screen
  • ✅ Automatic corner snapping when released near corners
  • ✅ Safe area support (respects notches, status bars, etc.)
  • ✅ Configurable margins and thresholds
  • ✅ Automatic filtering of active publishers
  • ✅ Lifecycle management (windows disappear when participants stop publishing)

Individual Window Usage:

For more control, you can use individual draggable windows:

BaxcloudDraggableParticipantWindow(
  key: ValueKey('participant-id'),
  videoTrack: videoTrack,
  width: 120,
  height: 160,
  mirror: false,
  config: DraggableParticipantWindowConfig(
    initialCorner: 1, // Top-right
  ),
)

Viewers Counter Widget

The BaxcloudViewersCounterWidget is a standalone, customizable widget that displays the total number of participants (viewers + publishers) and can be clicked to manage participants (for hosts/co-hosts).

Standalone Usage:

// With BaxcloudLiveStreamingController
BaxcloudViewersCounterWidget(
  controller: myController,
  enableManagement: true,
  onTap: () {
    // Show participants management
  },
)

// With BaxcloudLiveStreamingSeatsController
BaxcloudViewersCounterWidget(
  seatsController: mySeatsController,
  enableManagement: mySeatsController.isHost || mySeatsController.isCoHost,
  onTap: () {
    // Show participants management
  },
)

Custom Styling:

BaxcloudViewersCounterWidget(
  controller: myController,
  backgroundColor: Colors.blue.withValues(alpha: 0.8),
  borderColor: Colors.white,
  textColor: Colors.white,
  iconColor: Colors.white,
  borderRadius: 25.0,
  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
  onTap: () {
    // Handle tap
  },
)

Custom Builder:

BaxcloudViewersCounterWidget(
  controller: myController,
  counterBuilder: (context, viewers, publishers, total, canManage, onTap) {
    return Container(
      // Your custom design
      child: Text('$total viewers'),
    );
  },
)

Integrated in Live Streaming Views:

The viewers counter is automatically shown in all live streaming views when showViewersCounter: true (default). You can customize it using the viewersCounterBuilder parameter:

BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  showViewersCounter: true, // Default: true
  viewersCounterBuilder: (context, viewers, publishers, total, canManage, onTap) {
    // Custom counter design
    return YourCustomCounterWidget(...);
  },
)

Using Pre-generated Tokens

For security, generate tokens on your server and pass them to the SDK:

// Server-side token generation (example)
final token = await generateTokenOnServer(
  userId: 'user123',
  roomName: 'my-room',
);

// Connect with pre-generated token
// serverUrl is still automatically fetched from server
await controller.connectAndStart(
  roomName: 'my-room',
  token: token, // Use pre-generated token
);

Custom UI

You can also build your own UI using the controller:

class MyLiveStreamingPage extends StatefulWidget {
  @override
  _MyLiveStreamingPageState createState() => _MyLiveStreamingPageState();
}

class _MyLiveStreamingPageState extends State<MyLiveStreamingPage> {
  late BaxcloudLiveStreamingController controller;

  @override
  void initState() {
    super.initState();
    controller = BaxcloudLiveStreamingController();
    controller.initialize(BaxCloudClient.instance);
    controller.addListener(() {
      setState(() {}); // Update UI on state changes
    });
    _connect();
  }

  Future<void> _connect() async {
    // serverUrl is automatically fetched from server
    await controller.connectAndStart(
      roomName: 'my-room',
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Display local video
          if (controller.localVideoTrack != null)
            BaxcloudVideoView(
              track: controller.localVideoTrack,
              fit: BoxFit.cover,
            ),
          
          // Display remote participants
          ...controller.remoteParticipants.map((participant) {
            final track = participant.videoTrackPublications
                .where((pub) => pub.subscribed && pub.track != null)
                .map((pub) => pub.track)
                .firstOrNull;
            
            return track != null
                ? BaxcloudVideoView(track: track)
                : SizedBox.shrink();
          }),
          
          // Custom controls
          Positioned(
            bottom: 20,
            child: Row(
              children: [
                IconButton(
                  icon: Icon(controller.isVideoEnabled 
                      ? Icons.videocam 
                      : Icons.videocam_off),
                  onPressed: controller.toggleVideo,
                ),
                IconButton(
                  icon: Icon(controller.isAudioEnabled 
                      ? Icons.mic 
                      : Icons.mic_off),
                  onPressed: controller.toggleAudio,
                ),
                IconButton(
                  icon: Icon(Icons.flip_camera_ios),
                  onPressed: controller.switchCamera,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

3. Calls

4. Signaling / Chat (Live Streaming & Calls)

Important: Real-time signaling works directly through data channels - NO server connection needed! Only the serverUrl is required.

Basic Signaling (No Server Required)

import 'package:baxcloud_client/baxcloud_client.dart';

final signaling = BaxcloudSignalingController();

// After connecting with your BaxcloudLiveStreamingController or BaxcloudCallsController, attach the room:
signaling.attachRoom(controller.room!);

// Listen to room data events for receiving messages
controller.room!.addListener(() {
  // Handle room events
});

// Handle data received event (forward data packets)
void onDataReceived(DataReceivedEvent event) {
  signaling.handleRemoteData(event.data);
}

// UI - Messages are sent/received directly through data channels!
Column(
  children: [
    Expanded(
      child: BaxcloudChatView(controller: signaling),
    ),
    BaxcloudMessageInput(
      onSend: (text) async {
        // Sends directly through data channels - no server needed!
        await signaling.sendText(
          content: text,
          roomId: controller.room!.name,
          senderId: controller.room!.localParticipant!.identity,
          senderName: controller.room!.localParticipant!.name,
          senderAvatarUrl: 'https://example.com/avatar.png',
        );
      },
    ),
  ],
);

How it works:

  • ✅ Messages are sent via data channels → Directly to the server
  • ✅ Server broadcasts messages to all participants via WebSocket
  • ✅ Clients receive through data events
  • No intermediate server needed - messages flow: Client → Server → All Clients

Live Chat Widget

The BaxcloudLiveChatWidget provides a live chat experience with vertical message list, smooth animations, and customizable styling.

Basic Usage:

BaxcloudLiveChatWidget(
  controller: signalingController,
  maxVisibleMessages: 50,
  showAvatars: true,
  showSenderNames: true,
  showTimestamps: false,
)

Custom Styling:

BaxcloudLiveChatWidget(
  controller: signalingController,
  backgroundColor: Colors.black.withValues(alpha: 0.6),
  textColor: Colors.white,
  senderNameColor: Colors.white70,
  fontSize: 14.0,
  senderNameFontSize: 12.0,
  borderRadius: 18.0,
  maxWidth: 280.0,
  padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
  messagePadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
  showAvatars: true,
  showSenderNames: true,
  showTimestamps: false,
)

Custom Message Builder:

BaxcloudLiveChatWidget(
  controller: signalingController,
  messageBuilder: (context, message) {
    return Container(
      // Your custom message design
      child: Text(message.content),
    );
  },
)

Complete Example:

Stack(
  children: [
    // Your live streaming video
    BaxcloudVideoView(track: videoTrack),
    
    // Live chat overlay
    Positioned(
      right: 8,
      top: 100,
      bottom: 100,
      child: SizedBox(
        width: 300,
        child: BaxcloudLiveChatWidget(
          controller: signalingController,
          maxVisibleMessages: 30,
          showAvatars: true,
          showSenderNames: true,
        ),
      ),
    ),
    
    // Message input
    Positioned(
      bottom: 0,
      left: 0,
      right: 0,
      child: BaxcloudMessageInput(
        onSend: (text) async {
          await signalingController.sendText(
            content: text,
            roomId: room.name,
            senderId: room.localParticipant!.identity,
            senderName: room.localParticipant!.name,
          );
        },
      ),
    ),
  ],
)

Key Points:

  • Real-time messaging works directly through data channels - no server connection needed
  • Only serverUrl is required - no need for your Node.js server
  • 📡 Server-side endpoints are optional - only for message persistence, history, moderation
  • 🔄 Use sendReaction for lightweight emoji reactions (lossy delivery)
  • 🎨 Customize chat with BaxcloudChatView.messageBuilder and BaxcloudMessageInput.builder

Optional: Server-Side Message Storage

For message persistence, history, and moderation, you can optionally store messages on your server:

// Optional: Store message on server for persistence/moderation
Future<void> storeMessageOnServer({
  required String roomId,
  required String senderId,
  required String senderName,
  required String content,
}) async {
  final response = await http.post(
    Uri.parse('http://localhost:3000/api/signaling/message'),
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({
      'roomId': roomId,
      'senderId': senderId,
      'senderName': senderName,
      'type': 'text',
      'content': content,
    }),
  );
}

// Send message through data channels (real-time - no server needed!)
await signaling.sendText(
  content: text,
  roomId: roomId,
  senderId: senderId,
  senderName: senderName,
);

// Optionally store on server (for history/moderation)
await storeMessageOnServer(
  roomId: roomId,
  senderId: senderId,
  senderName: senderName,
  content: text,
);

5. PK (Player Kill) Battle

PK Battle allows two streamers to compete in a split-screen format with real-time voting.

import 'package:baxcloud_client/baxcloud_client.dart';
import 'dart:convert';

// Create PK controller
final pkController = BaxcloudPKController();
pkController.initialize(BaxCloudClient.instance);

// Attach to your connected room (from BaxcloudLiveStreamingController or BaxcloudCallsController)
pkController.attachRoom(controller.room!);

// Start a PK battle
await pkController.startBattle(
  roomId: 'room-123',
  opponentUserId: 'opponent-456',
  opponentUsername: 'Opponent Name',
  opponentAvatarUrl: 'https://example.com/avatar.png',
  duration: Duration(minutes: 5), // Optional, default 5 minutes
);

// Handle remote PK events (in your room data callback)
void onData(List<int> data) {
  try {
    final decoded = jsonDecode(utf8.decode(data)) as Map<String, dynamic>;
    if (decoded['type'] == 'pk') {
      pkController.handleRemotePKEvent(
        decoded['event'] as String,
        decoded['data'] as Map<String, dynamic>,
      );
    }
  } catch (_) {}
}

// UI
BaxcloudPKBattleView(
  controller: pkController,
  room: controller.room!,
  localVideoTrack: controller.localVideoTrack,
  onVote: (userId) {
    print('Voted for $userId');
    // Optional: Track votes or show animations
  },
)

PK Battle Features

  • Split-screen view - Two streamers side by side
  • Real-time scoring - Vote-based scoring system
  • Timer - Countdown and battle duration timer
  • Winner display - Automatic winner determination
  • Voting buttons - Easy voting interface for viewers
  • Customizable UI - Widget builders for complete customization

PK Controller Methods

  • startBattle() - Start a new PK battle
  • joinBattle() - Join an existing battle
  • endBattle() - End the current battle
  • vote() - Vote for a participant (adds 1 point)
  • addScore() - Add custom score points
  • handleRemotePKEvent() - Handle remote PK events

6. Advanced Features

Screen Sharing

Share your screen during live streams or calls:

import 'package:baxcloud_client/baxcloud_client.dart';

// Create screen sharing controller
final screenSharing = ScreenSharingController();
screenSharing.attachRoom(controller.room!);

// Start screen sharing
await screenSharing.startScreenSharing(preserveCamera: true);

// Stop screen sharing and restore camera
await screenSharing.stopScreenSharing(restoreCamera: true);

// Toggle screen sharing
await screenSharing.toggleScreenSharing();

Network Quality Indicator

Monitor and display network connection quality:

// Create network quality monitor
final networkMonitor = NetworkQualityMonitor();
networkMonitor.attachRoom(controller.room!);

// Display network quality
NetworkQualityIndicator(
  monitor: networkMonitor,
  showLabel: true,
  showDetails: true,
)

Live Stats

Monitor live streaming statistics:

// Create live stats monitor
final statsMonitor = LiveStatsMonitor();
statsMonitor.attachRoom(controller.room!);

// Display live stats
BaxcloudLiveStatsWidget(
  monitor: statsMonitor,
  showViewerCount: true,
  showParticipantCount: true,
  showBitrate: true,
  showFPS: true,
  showResolution: true,
)

7. Session Management & Analytics

Get Active Sessions

List all active sessions/rooms:

// Get all active sessions
final sessions = await BaxCloudClient.instance.getActiveSessions();

// Filter by appId (optional, for future SaaS use)
final sessions = await BaxCloudClient.instance.getActiveSessions(appId: 'your-app-id');

// Each session contains:
// - roomName: String
// - numParticipants: int
// - creationTime: String (timestamp)
// - emptyTimeout: String? (timeout in seconds)
// - maxParticipants: String? (max participants allowed)
// - metadata: String? (room metadata)

Get Room Analytics

Get detailed analytics for a specific room:

// Get analytics for a specific room
final analytics = await BaxCloudClient.instance.getRoomAnalytics('room-name');

// Analytics includes:
// - room: Room information (name, participants, creation time, etc.)
// - participants: List of participants with details
// - statistics: Total participants, active participants, room age

Get All Rooms Analytics

Get analytics summary for all active rooms:

// Get analytics for all rooms
final analytics = await BaxCloudClient.instance.getRoomsAnalytics();

// Analytics includes:
// - summary: Summary statistics (total rooms, participants, averages)
// - rooms: Per-room analytics with participant counts and ages

Terminate Sessions

Terminate a specific session:

// Terminate a specific room
await BaxCloudClient.instance.terminateSession('room-name');

Terminate all active sessions:

// Terminate all active sessions
final result = await BaxCloudClient.instance.terminateAllSessions();

// Result includes:
// - total: Total number of sessions
// - successful: Number of successfully terminated sessions
// - failed: Number of failed terminations

Example:

// Get all active sessions
final sessions = await BaxCloudClient.instance.getActiveSessions();
print('Active sessions: ${sessions.length}');

// Get analytics for a specific room
final analytics = await BaxCloudClient.instance.getRoomAnalytics('my-room');
print('Participants: ${analytics['statistics']['totalParticipants']}');
print('Room age: ${analytics['statistics']['roomAge']}ms');

// Get summary analytics
final summary = await BaxCloudClient.instance.getRoomsAnalytics();
print('Total rooms: ${summary['summary']['totalRooms']}');
print('Total participants: ${summary['summary']['totalParticipants']}');
print('Average per room: ${summary['summary']['averageParticipantsPerRoom']}');

Note: Analytics endpoints provide data for active sessions only. For ended sessions, you would need to use LiveKit Cloud's Analytics API (requires Scale plan) or implement your own session history tracking.

8. Participant Management (Server-Side Authorization)

Mute/unmute participants with server-side authorization and logging:

import 'package:baxcloud_client/baxcloud_client.dart';

// Mute a participant's audio
await BaxCloudClient.instance.muteParticipantAudio(
  'room-123',
  'participant-456',
);

// Unmute a participant's audio
await BaxCloudClient.instance.unmuteParticipantAudio(
  'room-123',
  'participant-456',
);

// Mute a participant's video
await BaxCloudClient.instance.muteParticipantVideo(
  'room-123',
  'participant-456',
);

// Unmute a participant's video
await BaxCloudClient.instance.unmuteParticipantVideo(
  'room-123',
  'participant-456',
);

Note: These methods provide server-side authorization and logging. The actual muting is handled client-side via signaling. The server endpoints validate participant existence and provide authorization/logging for audit purposes.

Client-Side Muting (via Controllers):

For direct client-side muting (via signaling), use the controller methods:

// In live streaming controllers
await controller.muteParticipant('participant-456');
await controller.disableParticipantVideo('participant-456');
await controller.removeParticipant('participant-456');

// In calls controllers
await controller.muteRemoteParticipantAudio('participant-456');
await controller.muteRemoteParticipantVideo('participant-456');

Use Cases:

  • Moderation - Hosts/moderators can mute disruptive participants
  • Server-Side Logging - Track all mute/unmute actions for audit
  • Authorization - Validate participant existence before operations
  • Integration - Integrate with backend for custom moderation rules

9. Customizable Messages

The SDK uses ScaffoldMessenger to show success/error messages for participant actions. You can customize or disable these messages using BaxcloudMessageHandler.

Disable Messages

To disable all messages:

BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  messageHandler: const BaxcloudMessageHandler(enabled: false),
)

Custom Message Widget

To use a custom widget for messages:

BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  messageHandler: BaxcloudMessageHandler(
    messageBuilder: (context, message, type) {
      // Your custom message widget
      return Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: type == BaxcloudMessageType.error ? Colors.red : Colors.green,
          borderRadius: BorderRadius.circular(8),
        ),
        child: Text(
          message,
          style: const TextStyle(color: Colors.white),
        ),
      );
    },
  ),
)

Custom Message Callback

To handle messages with a custom callback (e.g., show a dialog, log to console):

BaxcloudLiveStreamingView(
  controller: controller,
  roomName: 'my-room',
  messageHandler: BaxcloudMessageHandler(
    onMessage: (context, message, type) {
      // Your custom handling
      print('Message: $message (Type: $type)');
      // Or show a dialog, toast, etc.
    },
  ),
)

Message Types

The message handler supports different message types:

  • BaxcloudMessageType.success - Green background, 2 seconds duration
  • BaxcloudMessageType.error - Red background, 4 seconds duration
  • BaxcloudMessageType.warning - Orange background, 3 seconds duration
  • BaxcloudMessageType.info - Default background, 2 seconds duration

Available in:

  • BaxcloudLiveStreamingView
  • BaxcloudVoiceLiveStreamingView
  • BaxcloudParticipantsListWidget

3. Calls

1-to-1 Video Call

// Create controller
final controller = BaxcloudCallsController();
controller.initialize(BaxCloudClient.instance);

// Use the UI kit
BaxcloudVideoCallView(
  controller: controller,
  roomName: 'call-room-123',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  onConnected: () {
    print('Call connected');
  },
  onCallEnded: () {
    print('Call ended');
    Navigator.pop(context);
  },
  onError: (error) {
    print('Error: $error');
  },
)

1-to-1 Voice Call

// Create controller
final controller = BaxcloudCallsController();
controller.initialize(BaxCloudClient.instance);

// Use the UI kit
BaxcloudVoiceCallView(
  controller: controller,
  roomName: 'call-room-123',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  onConnected: () {
    print('Call connected');
  },
  onCallEnded: () {
    print('Call ended');
    Navigator.pop(context);
  },
)

Group Video Call

// Create controller
final controller = BaxcloudCallsController();
controller.initialize(BaxCloudClient.instance);

// Use the UI kit
BaxcloudGroupVideoCallView(
  controller: controller,
  roomName: 'group-call-room-123',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  maxParticipantsPerRow: 3, // Optional, default is 3
  onConnected: () {
    print('Group call connected');
  },
  onCallEnded: () {
    print('Call ended');
    Navigator.pop(context);
  },
)

Group Voice Call

// Create controller
final controller = BaxcloudCallsController();
controller.initialize(BaxCloudClient.instance);

// Use the UI kit
BaxcloudGroupVoiceCallView(
  controller: controller,
  roomName: 'group-voice-call-room-123',
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
  maxParticipantsPerRow: 3, // Optional, default is 3
  onConnected: () {
    print('Group voice call connected');
  },
  onCallEnded: () {
    print('Call ended');
    Navigator.pop(context);
  },
)

Advanced Usage with BaxcloudCallsController

// Create and initialize controller
final controller = BaxcloudCallsController();
controller.initialize(BaxCloudClient.instance);

// Connect and start a video call
// serverUrl is automatically fetched from server
await controller.connectAndStart(
  roomName: 'call-room-123',
  callType: CallType.video, // or CallType.voice
  userId: 'user123',
  username: 'John Doe',
  avatarUrl: 'https://example.com/avatar.jpg',
);

// Control the call
controller.toggleVideo(); // Toggle video on/off
controller.toggleAudio(); // Toggle audio on/off
controller.toggleMute(); // Mute/unmute
controller.switchCamera(); // Switch between front/back camera
controller.switchToVideo(); // Switch from voice to video
controller.switchToVoice(); // Switch from video to voice

// Check call state
if (controller.isGroupCall) {
  print('Group call with ${controller.participantCount} participants');
}

// End the call
await controller.endCall();

Configuration Options

BaxCloudConfig

Parameter Type Required Description
apiKey String Yes API Key for SaaS identification
appId String Yes App ID for SaaS identification
customServerUrl String No Custom server URL (overrides default live.baxcloud.com)
serverUrl String No Server URL (automatically fetched from server - managed by SDK)
userId String No Default user ID
username String No Default username
avatarUrl String No Default avatar URL
metadata Map<String, dynamic> No Additional metadata

BaxcloudLiveStreamingController

The controller provides the following state:

  • isConnecting - Whether the room is currently connecting
  • isConnected - Whether the room is connected
  • isPublishing - Whether publishing media
  • isVideoEnabled - Whether video is enabled
  • isAudioEnabled - Whether audio is enabled
  • isFrontCamera - Whether using front camera
  • room - Current room instance
  • localVideoTrack - Local video track
  • localAudioTrack - Local audio track
  • remoteParticipants - List of remote participants

BaxcloudCallsController

The controller provides the following state:

  • isConnecting - Whether the room is currently connecting
  • isConnected - Whether the room is connected
  • isPublishing - Whether publishing media
  • isVideoEnabled - Whether video is enabled
  • isAudioEnabled - Whether audio is enabled
  • isFrontCamera - Whether using front camera
  • isMuted - Whether call is muted
  • callType - Current call type (video or voice)
  • isGroupCall - Whether it's a group call (more than 2 participants)
  • participantCount - Number of participants (including self)
  • room - Current room instance
  • localVideoTrack - Local video track
  • localAudioTrack - Local audio track
  • remoteParticipants - List of remote participants

API Reference

BaxCloudClient

init(BaxCloudConfig config)

Initialize the SDK with configuration.

connectToRoom({...})

Connect to a room.

Parameters:

  • roomName (required) - Room name
  • userId (optional) - User ID
  • username (optional) - Username
  • avatarUrl (optional) - Avatar URL
  • metadata (optional) - Additional metadata
  • token (optional) - Pre-generated token (if not provided, will be fetched from server)

Note: serverUrl is automatically fetched from your server. You should not pass it directly.

BaxcloudLiveStreamingController

initialize(BaxCloudClient client)

Initialize the controller with BaxCloud client.

connectAndStart({...})

Connect to a room and start streaming.

startPublishing({enableVideo, enableAudio})

Start publishing media.

stopPublishing()

Stop publishing media.

toggleVideo()

Toggle video on/off.

toggleAudio()

Toggle audio on/off.

switchCamera()

Switch between front/back camera.

disconnect()

Disconnect from room.

BaxcloudCallsController

initialize(BaxCloudClient client)

Initialize the controller with BaxCloud client.

connectAndStart({...})

Connect to a room and start a call.

Parameters:

  • roomName (required) - Room name
  • callType (required) - Call type (CallType.video or CallType.voice)
  • userId (optional) - User ID
  • username (optional) - Username
  • avatarUrl (optional) - Avatar URL
  • metadata (optional) - Additional metadata
  • token (optional) - Pre-generated token (if not provided, will be fetched from server)

Note: serverUrl is automatically fetched from your server. You should not pass it directly.

startPublishing({enableVideo, enableAudio})

Start publishing media.

stopPublishing()

Stop publishing media.

toggleVideo()

Toggle video on/off.

toggleAudio()

Toggle audio on/off.

toggleMute()

Mute/unmute the call.

switchCamera()

Switch between front/back camera.

switchToVideo()

Switch from voice call to video call.

switchToVoice()

Switch from video call to voice call.

endCall()

End the call.

disconnect()

Disconnect from room.

Security Best Practices

  1. Always generate tokens server-side - Never expose your API secret in the client app
  2. Use HTTPS/WSS - Always use secure connections
  3. Validate room names - Ensure room names are validated on your server
  4. Implement authentication - Verify user identity before issuing tokens

Server-Side Token Generation

Example server-side token generation (Node.js):

const { AccessToken } = require('livekit-server-sdk'); // Internal dependency

function generateToken(userId, roomName, apiKey, apiSecret) {
  const at = new AccessToken(apiKey, apiSecret, {
    identity: userId,
    name: userId,
  });
  
  at.addGrant({
    roomJoin: true,
    room: roomName,
    canPublish: true,
    canSubscribe: true,
  });
  
  return at.toJwt();
}

Troubleshooting

Connection Issues

  • Ensure your server URL is correct (format: wss://your-server.com)
  • Check that your API key and secret are valid
  • Verify network connectivity
  • Check server logs for errors

Video/Audio Issues

  • Request camera and microphone permissions
  • Check device capabilities
  • Ensure tracks are properly published
  • Verify room permissions

Token Issues

  • Ensure tokens are generated with correct API key/secret
  • Check token expiration time
  • Verify token grants (canPublish, canSubscribe)
  • Ensure room name matches in token

Examples

See the /example folder for complete working examples.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

See LICENSE file for details.

Support

For issues and questions, please open an issue on GitHub.

Libraries

baxcloud_client
BaxCloud Flutter SDK