https://github.com/100mslive/100ms-flutter
Flutter Live Streaming, Video Conferencing SDK & Sample App
https://github.com/100mslive/100ms-flutter
audio chat conference dart ffmpeg flutter hacktoberfest hls live meeting player rtmp streaming video webrtc
Last synced: 3 months ago
JSON representation
Flutter Live Streaming, Video Conferencing SDK & Sample App
- Host: GitHub
- URL: https://github.com/100mslive/100ms-flutter
- Owner: 100mslive
- License: mit
- Created: 2021-07-01T08:30:43.000Z (almost 4 years ago)
- Default Branch: main
- Last Pushed: 2025-02-13T13:04:11.000Z (5 months ago)
- Last Synced: 2025-04-12T23:42:10.721Z (3 months ago)
- Topics: audio, chat, conference, dart, ffmpeg, flutter, hacktoberfest, hls, live, meeting, player, rtmp, streaming, video, webrtc
- Language: Dart
- Homepage: https://www.100ms.live/
- Size: 176 MB
- Stars: 152
- Watchers: 8
- Forks: 78
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: .github/CONTRIBUTING.md
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Support: .github/SUPPORT.md
Awesome Lists containing this project
README
[](https://pub.dev/packages/hmssdk_flutter)
[](https://github.com/100mslive/100ms-flutter/actions/workflows/build.yml)
[](https://www.100ms.live/)
[](https://docs.100ms.live/flutter/v2/foundation/basics)
[](https://100ms.live/discord)
[](https://appdistribution.firebase.dev/i/b623e5310929ab70)
[](https://testflight.apple.com/join/Uhzebmut)
[](https://dashboard.100ms.live/register)# 100ms Flutter SDK 🎉
![]()
Integrate Real Time Audio and Video conferencing, Interactive Live Streaming, and Chat in your apps with 100ms Flutter SDK.
With support for HLS and RTMP Live Streaming and Recording, Picture-in-Picture (PiP), CallKit, VoIP, one-to-one Video Call Modes, Audio Rooms, Video Player and much more, add immersive real-time communications to your apps.
| Package | Version |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| hms_room_kit | [](https://pub.dev/packages/hms_room_kit) |
| hmssdk_flutter | [](https://pub.dev/packages/hmssdk_flutter) |💻 Checkout the 100ms Flutter SDK [here](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter)
❤️ Checkout the 100ms Prebuilt [here](https://github.com/100mslive/100ms-flutter/tree/main/packages/hms_room_kit)
🧱 The Prebuilt QuickStart Guide is [available here](https://www.100ms.live/docs/flutter/v2/quickstart/prebuilt).
📖 Read the Complete Documentation here: https://www.100ms.live/docs/flutter/v2/guides/quickstart
📲 Download the Sample iOS app here:
🤖 Download the Sample Android app here:
100ms Flutter apps are also available on the App Stores, do download them and try it out.
## 🚂 Setup Guide
1. Sign up on & visit the Developer tab to access your credentials.
2. Get familiarized with [Tokens & Security here](https://www.100ms.live/docs/flutter/v2/foundation/security-and-tokens)
3. Complete the steps in [Auth Token Quick Start Guide](https://www.100ms.live/docs/flutter/v2/guides/token)
4. Get the HMSSDK via [pub.dev](https://pub.dev/packages/hmssdk_flutter). Add the `hmssdk_flutter` to your pubspec.yaml.
📖 Do refer the Complete [Installation Guide here](https://www.100ms.live/docs/flutter/v2/features/integration).
## 🏃♀️ How to run the Example App
The full featured Example app is [available here](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter/example).
🚀 We have added explanations & recommended usage guide for different features, UI Layouts, Data Store, etc in [Example app ReadMe](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter/example).
To run the Example App on your system follow these steps -
1. Clone the 100ms Flutter [Github Repo](https://github.com/100mslive/100ms-flutter.git)
2. In project root, run `flutter pub get` to build the Flutter package
3. Change directory to `example` folder
4. Now, to run the app, simply execute the `flutter run` command to run the app on a connected device, or iOS simulator, or Android Emulator.
5. For running on an iOS Device (iPhone or iPad), ensure you have set the Correct Development Team in Xcode Signing & Capabilities section.The default [Example app](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter/example) uses Provider State Management library. For other implementations please check out -
1. [Simple QuickStart](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/flutter-quickstart-app)
2. [Bloc](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/bloc)
3. [Getx](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/getx)
4. [Mobx](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/mobx)
5. [Riverpod](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/riverpod)
6. [Audio Room App](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/flutter-audio-room-quickstart)
7. [CallKit & VoIP](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/hms-callkit-app)
8. [Flutterflow with Prebuilt](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/flutterflow-prebuilt-quickstart)
9. [Live Stream with HLS](https://github.com/100mslive/100ms-flutter/tree/main/sample%20apps/flutter-hls-quickstart)
10. [Full featured App](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter/example)
![]()
![]()
![]()
![]()
![]()
![]()
## ☝️ Minimum Configuration
- Support for Flutter 3.3.0 or above
- Support for Android API level 21 or above
- Support for Java 17 or above
- Support for iOS 12 or above
- Xcode 12 or above## 🤝 Recommended Configuration
- Flutter 3.13.0 or above
- Android API level 34 or above
- Java 17 or above
- iOS 16 or above
- Xcode 15 or above## 📱 Supported Devices
- The Android SDK supports Android API level 21 and above. It is built for armeabi-v7a, arm64-v8a, x86, and x86_64 architectures. Devices running Android OS 12 or above is recommended.
- iPhone & iPads with iOS version 12 or above are supported. Devices running iOS 16 or above is recommended.
## 🤖 [Android Permissions](https://www.100ms.live/docs/flutter/v2/how-to-guides/install-the-sdk/integration#android)
Complete Permissions Guide is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/install-the-sdk/integration).
Add the following permissions in the Android's `AndroidManifest.xml` file -
```xml
```
## 🍎 [iOS Permissions](https://www.100ms.live/docs/flutter/v2/how-to-guides/install-the-sdk/integration#ios)
Add following permissions in iOS Info.plist file
```xml
NSMicrophoneUsageDescription
{YourAppName} wants to use your microphoneNSCameraUsageDescription
{YourAppName} wants to use your cameraNSLocalNetworkUsageDescription
{YourAppName} App wants to use your local network
```## 🧐 [Key Concepts](https://www.100ms.live/docs/get-started/v2/get-started/concepts/basics)
- `Room` A room is the basic object that 100ms SDKs return on successful connection. This contains references to peers, tracks and everything you need to render a live a/v or live streaming app.
- `Peer` A peer is the object returned by 100ms SDKs that contains all information about a user - name, role, video track etc.
- `Track` A track is a segment of media (audio/video) captured from the peer's camera and microphone. Peers in a session publish local tracks and subscribe to remote tracks from other peers.
- `Role` A role defines who can a peer see/hear, the quality at which they publish their video, whether they have permissions to publish video/screenshare, mute someone, change someone's role.

## ♻️ [Setup Update Listeners](https://www.100ms.live/docs/flutter/v2/how-to-guides/listen-to-room-updates/update-listeners)
100ms SDK provides callbacks to the client app about any change or update happening in the room after a user has joined by implementing `HMSUpdateListener`. These updates can be used to render the video on the screen or to display other info regarding the room. Let's take a look at the flowchart.

```dart
/// 100ms SDK provides callbacks to the client app about any change or update happening in the room after a user has joined by implementing HMSUpdateListener.
/// These updates can be used to render the video on the screen or to display other info regarding the room.
abstract class HMSUpdateListener {/// This will be called on a successful JOIN of the room by the user
///
/// This is the point where applications can stop showing their loading state
/// [room]: the room which was joined
void onJoin({required HMSRoom room});/// This will be called whenever there is an update on an existing peer
/// or a new peer got added/existing peer is removed.
///
/// This callback can be used to keep a track of all the peers in the room
/// [peer]: the peer who joined/left or was updated
/// [update]: the triggered update type. Should be used to perform different UI Actions
void onPeerUpdate({required HMSPeer peer, required HMSPeerUpdate update});/// This is called when there are updates on an existing track
/// or a new track got added/existing track is removed
///
/// This callback can be used to render the video on screen whenever a track gets added
/// [track]: the track which was added, removed or updated
/// [trackUpdate]: the triggered update type
/// [peer]: the peer for which track was added, removed or updated
void onTrackUpdate({required HMSTrack track, required HMSTrackUpdate trackUpdate, required HMSPeer peer});/// This will be called when there is an error in the system
/// and SDK has already retried to fix the error
/// [error]: the error that occurred
void onHMSError({required HMSException error});/// This is called when there is a new broadcast message from any other peer in the room
///
/// This can be used to implement chat is the room
/// [message]: the received broadcast message
void onMessage({required HMSMessage message});/// This is called every 1 second with a list of active speakers
///
/// ## A HMSSpeaker object contains -
/// - peerId: the peer identifier of HMSPeer who is speaking
/// - trackId: the track identifier of HMSTrack which is emitting audio
/// - audioLevel: a number within range 1-100 indicating the audio volume
///
/// A peer who is not present in the list indicates that the peer is not speaking
///
/// This can be used to highlight currently speaking peers in the room
/// [speakers] the list of speakers
void onUpdateSpeakers({required List updateSpeakers});/// This is called when there is a change in any property of the Room
///
/// [room]: the room which was joined
/// [update]: the triggered update type. Should be used to perform different UI Actions
void onRoomUpdate({required HMSRoom room, required HMSRoomUpdate update});/// when network or some other error happens it will be called
void onReconnecting();/// when you are back in the room after reconnection
void onReconnected();/// This is called when someone asks for a change or role
///
/// for eg. admin can ask a peer to become host from guest.
/// this triggers this call on peer's app
void onRoleChangeRequest({required HMSRoleChangeRequest roleChangeRequest});/// when someone requests for track change of yours be it video or audio this will be triggered
/// [hmsTrackChangeRequest] request instance consisting of all the required info about track change
void onChangeTrackStateRequest({required HMSTrackChangeRequest hmsTrackChangeRequest});/// when someone kicks you out or when someone ends the room at that time it is triggered
/// [hmsPeerRemovedFromPeer] it consists of info about who removed you and why.
void onRemovedFromRoom({required HMSPeerRemovedFromPeer hmsPeerRemovedFromPeer});/// whenever a new audio device is plugged in or audio output is changed we
/// get the onAudioDeviceChanged update
/// This callback is only fired on Android devices. On iOS, this callback will not be triggered.
/// - Parameters:
/// - currentAudioDevice: Current audio output route
/// - availableAudioDevice: List of available audio output devices
void onAudioDeviceChanged(
{HMSAudioDevice? currentAudioDevice,
List? availableAudioDevice});/// Whenever a user joins a room [onSessionStoreAvailable] is fired to get an instance of [HMSSessionStore]
/// which can be used to perform session metadata operations
/// - Parameters:
/// - hmsSessionStore: An instance of HMSSessionStore which will be used to call session metadata methods
void onSessionStoreAvailable(
{HMSSessionStore? hmsSessionStore});/// Upon joining a room with existing peers, onPeerListUpdated will be called with the list of peers present
/// in the room instead of getting onPeerUpdate for each peer in the room.
/// Subsequent peer joins/leaves would be notified via both onPeerUpdate and onPeerListUpdated
void onPeerListUpdate(
{required List addedPeers, required List removedPeers});
}
```## 🤔 [How to listen to Peer & Track updates?](https://www.100ms.live/docs/flutter/v2/features/update-listener-enums)
The 100ms SDK sends updates to the application about any change in HMSPeer, HMSTrack, HMSRoom, etc via the callbacks in `HMSUpdateListener`.
The methods of HMSUpdateListener are invoked to notify updates happening in the room like a peer join/leave, track mute/unmute etc.
More information about Update Listeners is [available here](https://www.100ms.live/docs/flutter/v2/features/update-listeners).
The following are the different types of updates that are emitted by the SDK -
```txt
HMSPeerUpdate
.peerJoined: A new peer joins the Room
.peerLeft: An existing peer leaves the Room
.roleUpdated: When a peer's Role has changed
.metadataChanged: When a peer's metadata has changed
.nameChanged: When a peer's name has changedHMSTrackUpdate
.trackAdded: A new track (audio or video) is added to the Room
.trackRemoved: An existing track is removed from the Room
.trackMuted: A track of a peer is muted
.trackUnMuted: A muted track of a peer was unmuted
.trackDegraded: When track is degraded due to bad network conditions
.trackRestored: When a degraded track is restored when optimal network conditions are available againHMSRoomUpdate
.roomMuted: When room is muted to due external interruption like a Phone Call
.roomUnmuted: When a muted room is unmuted when external interruption has ended
.serverRecordingStateUpdated: When Server recording state is updated
.browserRecordingStateUpdated: When Browser recording state is changed
.rtmpStreamingStateUpdated: When RTMP is started or stopped
.hlsStreamingStateUpdated: When HLS is started or stopped
.hlsRecordingStateUpdated: When HLS recording state is updated
```## 🙏 [Join a Room](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/join)
To join and interact with others in audio or video call, the user needs to `join` a `room`.
To join a Room, your app should have -
1. User Name - The name which should be displayed to other peers in the room.
2. [Authentication Token](https://www.100ms.live/docs/get-started/v2/get-started/security-and-tokens) - The Client side Authentication Token generated by the Token Service.### 🔑 [Get Authentication token](https://www.100ms.live/docs/flutter/v2/quickstart/quickstart#fetch-token-to-join-the-room)
To join a room we need an authentication token as mentioned above. There are two methods to get the token:
#### Fetch token using room-code method (Recommended)
We can get the authentication token using room-code from meeting URL.
Let's understand the subdomain and code from the sample URL
In this URL: `https://public.app.100ms.live/meeting/xvm-wxwo-gbl`- Subdomain is `public`
- Room code is `xvm-wxwo-gbl`Now to get the room-code from meeting URL we can write our own logic or use the `getCode` method from [here](https://github.com/100mslive/100ms-flutter/blob/main/packages/hmssdk_flutter/example/lib/room_service.dart)
To generate token we will be using `getAuthTokenByRoomCode` method of `HMSSDK`. This method has `roomCode` as a required
parameter, `userId` & `endPoint` as optional parameter.> This method should be called after calling the `build` method.
Let's checkout the implementation:
```dart
//This returns an object of Future which can be either
//of HMSException type or String? type based on whether
//method execution is completed successfully or notdynamic authToken = await hmsSDK.getAuthTokenByRoomCode(roomCode: 'YOUR_ROOM_CODE');
if(authToken is String){
HMSConfig roomConfig = HMSConfig(
authToken: authToken,
userName: userName,
);hmsSDK.join(config: roomConfig);
}
else if(authToken is HMSException){
// Handle the error
}
```#### Get temporary token from dashboard
To test audio/video functionality, you need to connect to a 100ms room; please check the following steps for the same:
1. Navigate to your [100ms dashboard](https://dashboard.100ms.live/dashboard) or [create an account](https://dashboard.100ms.live/register) if you don't have one.
2. Use the `Video Conferencing Starter Kit` to create a room with a default template assigned to it to test this app quickly.
3. Go to the [Rooms page](https://dashboard.100ms.live/rooms) in your dashboard, click on the `Room Id` of the room you created above, and click on the `Join Room` button on the top right.
4. In the Join with SDK section you can find the `Auth Token for role` ; you can click on the 'copy' icon to copy the authentication token.> Token from 100ms dashboard is for testing purposes only, For production applications you must generate tokens on your own server.
> Refer to the [Management Token section](/flutter/v2/foundation/security-and-tokens#management-token) in Authentication and Tokens guide for more information.You can also optionally pass these fields in the HMSSDK constructor -
1. Track Settings - Such as joining a Room with Muted Audio or Video using the `HMSTrackSetting` object. More information is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/join#join-with-muted-audio-video).
2. User Metadata - This can be used to pass any additional metadata associated with the user using `metadata` of `HMSConfig` object. For Eg: user-id mapping at the application side. More information is available [here](https://www.100ms.live/docs/flutter/v2/how-to-guides/interact-with-room/peer/peer-metadata-update).
More information about Joining a Room is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/join).
```dart
// create a class that implements `HMSUpdateListener` and acts as a data source for our UI
class Meeting implements HMSUpdateListener {late HMSSDK hmsSDK;
Meeting() {
initHMSSDK();
}void initHMSSDK() async {
hmsSDK = HMSSDK(); // create an instance of `HMSSDK` by invoking it's constructor
await hmsSDK.build(); // ensure to await while invoking the `build` method
hmsSDK.addUpdateListener(this); // `this` value corresponds to the instance implementing HMSUpdateListener
HMSConfig config = HMSConfig(authToken: 'eyJH5c', // client-side token generated from your token service
userName: 'John Appleseed');
hmsSDK.join(config: config); // Now, we are primed to join the room. All you have to do is call `join` by passing the `config` object.
}... // implement methods of HMSUpdateListener
}
```## 🎞 [Display a Track](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/render-video/overview)
It all comes down to this. All the setup so far has been done so that we can show live-streaming videos in our beautiful app.
100ms Flutter Package provides the `HMSVideoView` widget that renders the video on the screen.
We simply have to pass a Video Track object to the `HMSVideoView` to begin automatic rendering of Live Video Stream.
We can also optionally pass props like `key`, `scaleType`, `mirror` to customize the `HMSVideoView` widget.
```dart
HMSVideoView(
track: videoTrack,
key: Key(videoTrack.trackId),
),
```More information about displaying a Video is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/render-video/overview).
## 👋 [Leave Room](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/leave)
Once you're done with the meeting and want to exit, call leave on the HMSSDK instance that you created to join the room.
Before calling leave, remove the `HMSUpdateListener` instance as:
```dart
// updateListener is the instance of class in which HMSUpdateListener is implemented
hmsSDK.removeUpdateListener(updateListener);
```To leave the meeting, call the `leave` method of `HMSSDK` and pass the `hmsActionResultListener` parameter to get a success callback from SDK in the `onSuccess` override method as follow.
> You will need to implement the `HMSActionResultListener` interface in a class to get `onSuccess` and `onException` callback.
> To know about how to implement `HMSActionResultListener` check the docs [here](https://www.100ms.live/docs/flutter/v2/how-to-guides/listen-to-room-updates/action-result-listeners)```dart
class Meeting implements HMSActionResultListener{
//this is the instance of class implementing HMSActionResultListener
await hmsSDK.leave(hmsActionResultListener: this);@override
void onSuccess(
{HMSActionResultListenerMethod methodType = HMSActionResultListenerMethod.unknown, Map? arguments}) {
switch (methodType) {
case HMSActionResultListenerMethod.leave:
// Room leaved successfully
// Clear the local room state
break;
...
}
}
}
```More information about Leaving a Room is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/leave).
## 🛤 HMSTracks Explained
`HMSTrack` is the super-class of all the tracks that are used inside `HMSSDK`. Its hierarchy looks like this -
```dart
HMSTrack
- AudioTrack
- LocalAudioTrack
- RemoteAudioTrack
- VideoTrack
- LocalVideoTrack
- RemoteVideoTrack
```## 🛤 How to know the type and source of Track?
HMSTrack contains a field called source which denotes the source of the Track.
`Source` can have the following values -
- `regular` For normal Audio or Video being published by peers
- `screen` For Screenshare track whenver a peer starts Broadcasting their Screen in a Room
- `plugin` For a custom Audio or Video plugin being used in RoomTo know the type of track, check the value of type which would be one of the enum values - `AUDIO` or `VIDEO`
## 📨 [Chat Messaging](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/chat)
You can send a chat or any other kind of message from a local peer to all remote peers in the room.
To send a message first create an instance of `HMSMessage` object.
Add the information to be sent in the `message` property of `HMSMessage`.
Then use the `sendBroadcastMessage` function on the instance of HMSSDK for broadcast message, `sendGroupMessage` for group message and `sendDirectMessage` for direct message.
When you(the local peer) receives a message from others(any remote peer), `void onMessage({required HMSMessage message})` function of `HMSUpdateListener` is invoked.
More information about Chat Messaging is [available here](https://www.100ms.live/docs/flutter/v2/how-to-guides/set-up-video-conferencing/chat).
```dart
// onMessage is HMSUpdateListener method called when a new message is received
@override
void onMessage({required HMSMessage message}) {
//Here we will receive messages sent by other peers
}///[message]: Message to be sent
///[type]: Message type(More about this at the end)
///[hmsActionResultListener]: instance of class implementing HMSActionResultListener
//Here this is an instance of class that implements HMSActionResultListener i.e. Meeting
hmsSDK.sendBroadcastMessage(
message: message,
type: type,
hmsActionResultListener: this);///[message]: Message to be sent
///[peerTo]: Peer to whom message needs to be sent
///[type]: Message type(More about this at the end)
///[hmsActionResultListener]: instance of class implementing HMSActionResultListener
//Here this is an instance of class that implements HMSActionResultListener i.e. Meeting
hmsSDK.sendDirectMessage(
message: message,
peerTo: peerTo,
type: type,
hmsActionResultListener: this);///[message]: Message to be sent
///[hmsRolesTo]: Roles to which this message needs to be sent
///[type]: Message type(More about this at the end)
///[hmsActionResultListener]: instance of class implementing HMSActionResultListener
//Here this is an instance of class that implements HMSActionResultListener i.e. Meeting
hmsSDK.sendGroupMessage(
message: message,
hmsRolesTo: rolesToSendMessage,
type: type,
hmsActionResultListener: this);@override
void onSuccess(
{HMSActionResultListenerMethod methodType =
HMSActionResultListenerMethod.unknown,
Map? arguments}) {switch (methodType) {
case HMSActionResultListenerMethod.sendBroadcastMessage:
//Broadcast Message sent successfully
break;case HMSActionResultListenerMethod.sendGroupMessage:
//Group Message sent successfully
break;case HMSActionResultListenerMethod.sendDirectMessage:
//Direct Message sent successfully
break;
...
}
}@override
void onException(
{HMSActionResultListenerMethod methodType =
HMSActionResultListenerMethod.unknown,
Map? arguments,
required HMSException hmsException}) {switch (methodType) {
case HMSActionResultListenerMethod.sendBroadcastMessage:
// Check the HMSException object for details about the error
break;case HMSActionResultListenerMethod.sendGroupMessage:
// Check the HMSException object for details about the error
break;case HMSActionResultListenerMethod.sendDirectMessage:
// Check the HMSException object for details about the error
break;
...
}}
```🧱 The Prebuilt QuickStart Guide is [available here](https://www.100ms.live/docs/flutter/v2/quickstart/prebuilt).
📖 Do refer the Complete Documentation Guide [available here](https://www.100ms.live/docs/flutter/v2/guides/quickstart).
🏃♀️ Checkout the Example app implementation [available here](https://github.com/100mslive/100ms-flutter/tree/main/example).
🚀 We have added explanations & recommended usage guide for different features, UI Layouts, Data Store, etc in [Example app ReadMe](https://github.com/100mslive/100ms-flutter/tree/main/packages/hmssdk_flutter/example).
100ms Flutter apps are released on the App Stores, you can download them here:
📲 iOS app on Apple App Store:
🤖 Android app on Google Play Store: