pub package

UTD Media Client — Flutter SDK

Use this SDK to add realtime video, audio and data features to your Flutter app. By connecting to the UTD Media Engine, you can quickly build applications such as live streaming, voice rooms, video calls, and multi-modal experiences with just a few lines of code.

This package is published to pub.dev as utd_media_client.

Docs

More docs and guides are available at https://utdsoftware.com.

Supported platforms

The UTD Media Client for Flutter is designed to work across all platforms supported by Flutter:

  • Android
  • iOS
  • Web
  • macOS
  • Windows
  • Linux

Installation

Include this package in your pubspec.yaml:

dependencies:
  utd_media_client: ^2.7.0

iOS

Camera and microphone usage need to be declared in your Info.plist file.

<dict>
  ...
  <key>NSCameraUsageDescription</key>
  <string>$(PRODUCT_NAME) uses your camera</string>
  <key>NSMicrophoneUsageDescription</key>
  <string>$(PRODUCT_NAME) uses your microphone</string>

Your application can still run the voice call when it is switched to the background if the background mode is enabled. Select the app target in Xcode, click the Capabilities tab, enable Background Modes, and check Audio, AirPlay, and Picture in Picture.

Your Info.plist should have the following entries.

<dict>
  ...
  <key>UIBackgroundModes</key>
  <array>
    <string>audio</string>
  </array>

For iOS, the minimum supported deployment target is 12.1. You will need to add the following to your Podfile.

platform :ios, '12.1'

You may need to delete Podfile.lock and re-run pod install after updating the deployment target.

Android

We require a set of permissions that need to be declared in your AppManifest.xml.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.your.package">
  <uses-feature android:name="android.hardware.camera" />
  <uses-feature android:name="android.hardware.camera.autofocus" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
  <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
  ...
</manifest>

Audio Modes

By default, we use the communication audio mode on Android which works best for two-way voice communication.

If your app is media-playback oriented and does not need the device's microphone, you can use the media audio mode which provides better audio quality.

Desktop support

In order to enable Flutter desktop development, please follow the official Flutter desktop setup instructions.

Usage

Connecting to a room, publish video & audio

final roomOptions = RoomOptions(
  adaptiveStream: true,
  dynacast: true,
  // ... your room options
);

final room = Room();

// you can use `prepareConnection` to speed up connection.
await room.prepareConnection(url, token);

await room.connect(url, token, roomOptions: roomOptions);

try {
  // video will fail when running in the ios simulator
  await room.localParticipant.setCameraEnabled(true);
} catch (error) {
  print('Could not publish video, error: $error');
}

await room.localParticipant.setMicrophoneEnabled(true);

Screen sharing

Screen sharing is supported across all platforms. You can enable it with:

room.localParticipant.setScreenShareEnabled(true);

Rendering video

Each track can be rendered separately with the provided VideoTrackRenderer widget.

VideoTrack? track;

@override
Widget build(BuildContext context) {
  if (track != null) {
    return VideoTrackRenderer(track);
  } else {
    return Container(
      color: Colors.grey,
    );
  }
}

Audio handling

Audio tracks are played automatically as long as you are subscribed to them.

Handling changes

The client makes it simple to build declarative UI that reacts to state changes. It notifies changes in two ways:

  • ChangeNotifier — generic notification of changes. Useful when building reactive UI that only cares about changes that may impact rendering.
  • EventsListener<Event> — listener pattern to listen to specific events.
class _RoomState extends State<RoomWidget> {
  late final EventsListener<RoomEvent> _listener = widget.room.createListener();

  @override
  void initState() {
    super.initState();
    widget.room.addListener(_onChange);

    _listener
      ..on<RoomDisconnectedEvent>((_) {
        // handle disconnect
      })
      ..on<ParticipantConnectedEvent>((e) {
        print("participant joined: ${e.participant.identity}");
      });
  }

  @override
  void dispose() {
    _listener.dispose();
    widget.room.removeListener(_onChange);
    super.dispose();
  }

  void _onChange() {
    setState(() {
      // your updates here
    });
  }
}

Mute, unmute local tracks

On LocalTrackPublications, you can control whether the track is muted by setting its muted property. Changing the mute status emits a TrackMutedEvent or TrackUnmutedEvent.

// mute track
trackPub.muted = true;

// unmute track
trackPub.muted = false;

End-to-end encryption

The SDK supports end-to-end encryption for audio/video data sent over the network. On native platforms E2EE works without extra setup; on Flutter web you need to compile an e2ee.worker.dart.js file.

License

Apache License 2.0