{"id":21580515,"url":"https://github.com/livekit/client-sdk-flutter","last_synced_at":"2025-04-06T08:13:29.197Z","repository":{"id":37930459,"uuid":"388948667","full_name":"livekit/client-sdk-flutter","owner":"livekit","description":"Flutter Client SDK for LiveKit","archived":false,"fork":false,"pushed_at":"2024-04-09T23:33:04.000Z","size":8295,"stargazers_count":204,"open_issues_count":51,"forks_count":102,"subscribers_count":16,"default_branch":"main","last_synced_at":"2024-04-10T18:20:48.405Z","etag":null,"topics":["conferencing","flutter","video","webrtc"],"latest_commit_sha":null,"homepage":"https://docs.livekit.io","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/livekit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-07-23T23:12:35.000Z","updated_at":"2024-08-12T07:40:12.027Z","dependencies_parsed_at":"2023-11-09T14:25:56.603Z","dependency_job_id":"ceacbad9-036f-4973-922f-324545d3ecc0","html_url":"https://github.com/livekit/client-sdk-flutter","commit_stats":null,"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-flutter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-flutter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-flutter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/livekit%2Fclient-sdk-flutter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/livekit","download_url":"https://codeload.github.com/livekit/client-sdk-flutter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247451665,"owners_count":20940944,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["conferencing","flutter","video","webrtc"],"created_at":"2024-11-24T14:01:20.488Z","updated_at":"2025-04-06T08:13:29.179Z","avatar_url":"https://github.com/livekit.png","language":"Dart","funding_links":[],"categories":["Dart"],"sub_categories":[],"readme":"\u003c!--BEGIN_BANNER_IMAGE--\u003e\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"/.github/banner_dark.png\"\u003e\n  \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"/.github/banner_light.png\"\u003e\n  \u003cimg style=\"width:100%;\" alt=\"The LiveKit icon, the name of the repository and some sample code in the background.\" src=\"https://raw.githubusercontent.com/livekit/client-sdk-flutter/main/.github/banner_light.png\"\u003e\n\u003c/picture\u003e\n\n\u003c!--END_BANNER_IMAGE--\u003e\n\n[![pub package](https://img.shields.io/pub/v/livekit_client?label=livekit_client\u0026color=blue)](https://pub.dev/packages/livekit_client)\n\n# LiveKit Flutter SDK\n\n\u003c!--BEGIN_DESCRIPTION--\u003e\nUse this SDK to add realtime video, audio and data features to your Flutter app. By connecting to \u003ca href=\"https://livekit.io/\"\u003eLiveKit\u003c/a\u003e Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with just a few lines of code.\n\u003c!--END_DESCRIPTION--\u003e\n\nThis package is published to pub.dev as [livekit_client](https://pub.dev/packages/livekit_client).\n\n## Docs\n\nMore Docs and guides are available at [https://docs.livekit.io](https://docs.livekit.io)\n\n## Supported platforms\n\nLiveKit client SDK for Flutter is designed to work across all platforms supported by Flutter:\n\n- Android\n- iOS\n- Web\n- macOS\n- Windows\n- Linux\n\n## Example app\n\nWe built a multi-user conferencing app as an example in the [example/](example/) folder. LiveKit is compatible cross-platform: you could join the same room using any of our supported realtime SDKs.\n\nOnline demo: https://livekit.github.io/client-sdk-flutter/\n\n## Installation\n\nInclude this package to your `pubspec.yaml`\n\n```yaml\n---\ndependencies:\n  livekit_client: \u003cversion\u003e\n```\n\n### iOS\n\nCamera and microphone usage need to be declared in your `Info.plist` file.\n\n```xml\n\u003cdict\u003e\n  ...\n  \u003ckey\u003eNSCameraUsageDescription\u003c/key\u003e\n  \u003cstring\u003e$(PRODUCT_NAME) uses your camera\u003c/string\u003e\n  \u003ckey\u003eNSMicrophoneUsageDescription\u003c/key\u003e\n  \u003cstring\u003e$(PRODUCT_NAME) uses your microphone\u003c/string\u003e\n```\n\nYour 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**.\n\nYour `Info.plist` should have the following entries.\n\n```xml\n\u003cdict\u003e\n  ...\n  \u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n  \u003carray\u003e\n    \u003cstring\u003eaudio\u003c/string\u003e\n  \u003c/array\u003e\n```\n\n#### Notes\n\nSince [xcode 14](https://developer.apple.com/news/upcoming-requirements/?id=06062022a) no longer supports 32bit builds, and our latest version is based on libwebrtc m104+ the iOS framework no longer supports 32bit builds, we strongly recommend upgrading to flutter 3.3.0+. if you are using flutter 3.0.0 or below, there is a high chance that your flutter app cannot be compiled correctly due to the missing i386 and arm 32bit framework [#132](https://github.com/livekit/client-sdk-flutter/issues/132) [#172](https://github.com/livekit/client-sdk-flutter/issues/172).\n\nYou can try to modify your `{projects_dir}/ios/Podfile` to fix this issue.\n\n```ruby\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n\n    target.build_configurations.each do |config|\n\n      # Workaround for https://github.com/flutter/flutter/issues/64502\n      config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' # \u003c= this line\n\n    end\n  end\nend\n```\n\nFor iOS, the minimum supported deployment target is `12.1`. You will need to add the following to your Podfile.\n\n```ruby\nplatform :ios, '12.1'\n```\n\nYou may need to delete `Podfile.lock` and re-run `pod install` after updating deployment target.\n\n### Android\n\nWe require a set of permissions that need to be declared in your `AppManifest.xml`. These are required by Flutter WebRTC, which we depend on.\n\n```xml\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\" package=\"com.your.package\"\u003e\n  \u003cuses-feature android:name=\"android.hardware.camera\" /\u003e\n  \u003cuses-feature android:name=\"android.hardware.camera.autofocus\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.CAMERA\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.RECORD_AUDIO\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.BLUETOOTH\" android:maxSdkVersion=\"30\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.BLUETOOTH_CONNECT\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" android:maxSdkVersion=\"30\" /\u003e\n  ...\n\u003c/manifest\u003e\n```\n\nFor using the bluetooth headset correctly on the android device, you need to add `permission_handler` to your project.\nAnd call the following code after launching your app for the first time.\n\n```dart\nimport 'package:permission_handler/permission_handler.dart';\n\nFuture\u003cvoid\u003e _checkPermissions() async {\n  var status = await Permission.bluetooth.request();\n  if (status.isPermanentlyDenied) {\n    print('Bluetooth Permission disabled');\n  }\n  status = await Permission.bluetoothConnect.request();\n  if (status.isPermanentlyDenied) {\n    print('Bluetooth Connect Permission disabled');\n  }\n}\n\nvoid main() async {\n  WidgetsFlutterBinding.ensureInitialized();\n  await _checkPermissions();\n  runApp(MyApp());\n}\n```\n\n#### Audio Modes\n\nBy default, we use the `communication` audio mode on Android which works best for two-way voice communication.\n\nIf your app is media playback oriented and does not need the use of the device's microphone, you can use the `media`\naudio mode which will provide better audio quality.\n\n```dart\nimport 'package:flutter_webrtc/flutter_webrtc.dart' as webrtc;\n\nFuture\u003cvoid\u003e _initializeAndroidAudioSettings() async {\n  await webrtc.WebRTC.initialize(options: {\n    'androidAudioConfiguration': webrtc.AndroidAudioConfiguration.media.toMap()\n  });\n  webrtc.Helper.setAndroidAudioConfiguration(\n      webrtc.AndroidAudioConfiguration.media);\n}\n\nvoid main() async {\n  await _initializeAudioSettings();\n  runApp(const MyApp());\n}\n```\n\nNote: the audio routing will become controlled by the system and cannot be manually changed with functions like\n`Hardware.selectAudioOutput`.\n\n### Desktop support\n\nIn order to enable Flutter desktop development, please follow [instructions here](https://docs.flutter.dev/desktop#set-up).\n\nOn Windows [VS 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=community\u0026rel=16) is needed (link in flutter docs will download VS 2022).\n\n## Usage\n\n### Connecting to a room, publish video \u0026 audio\n\n```dart\nfinal roomOptions = RoomOptions(\n  adaptiveStream: true,\n  dynacast: true,\n  // ... your room options\n)\n\nfinal room = Room();\n\n// you can use `prepareConnection` to speed up connection.\nawait room.prepareConnection(url, token);\n\nawait room.connect(url, token, roomOptions: roomOptions);\n\ntry {\n  // video will fail when running in ios simulator\n  await room.localParticipant.setCameraEnabled(true);\n} catch (error) {\n  print('Could not publish video, error: $error');\n}\n\nawait room.localParticipant.setMicrophoneEnabled(true);\n```\n\n### Screen sharing\n\nScreen sharing is supported across all platforms. You can enable it with:\n\n```dart\nroom.localParticipant.setScreenShareEnabled(true);\n```\n\n#### Android\n\nOn Android, you will have to use a [media projection foreground service](https://developer.android.com/develop/background-work/services/fg-service-types#media-projection).\n\nIn our example, we use the `flutter_background` package to handle this. In the app's AndroidManifest.xml file, declare the service with the appropriate types and permissions as following:\n\n```xml title=\"AndroidManifest.xml\"\n\u003cmanifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\u003e\n  \u003c!-- Required permissions for screen share --\u003e\n  \u003cuses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" /\u003e\n  \u003cuses-permission android:name=\"android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION\" /\u003e\n  \u003capplication\u003e\n    ...\n    \u003cservice\n        android:name=\"de.julianassmann.flutter_background.IsolateHolderService\"\n        android:enabled=\"true\"\n        android:exported=\"false\"\n        android:foregroundServiceType=\"mediaProjection\" /\u003e\n  \u003c/application\u003e\n\u003c/manifest\u003e\n```\n\nBefore starting the background service and enabling screen share, you **must** call `Helper.requestCapturePermission()` from `flutter_webrtc`, and only proceed if it returns true. [Refer to our example implementation for details.](https://github.com/livekit/client-sdk-flutter/blob/c97fa769260a3fefd146c0ee61e2ce5c30ce2010/example/lib/widgets/controls.dart#L159)\n\n#### iOS\n\nOn iOS, a broadcast extension is needed in order to capture screen content from\nother apps. See [setup guide](https://github.com/flutter-webrtc/flutter-webrtc/wiki/iOS-Screen-Sharing#broadcast-extension-quick-setup) for instructions.\n\n#### Desktop(Windows/macOS)\n\nOn dekstop you can use `ScreenSelectDialog` to select the window or screen you want to share.\n\n```dart\ntry {\n  final source = await showDialog\u003cDesktopCapturerSource\u003e(\n    context: context,\n    builder: (context) =\u003e ScreenSelectDialog(),\n  );\n  if (source == null) {\n    print('cancelled screenshare');\n    return;\n  }\n  print('DesktopCapturerSource: ${source.id}');\n  var track = await LocalVideoTrack.createScreenShareTrack(\n    ScreenShareCaptureOptions(\n      sourceId: source.id,\n      maxFrameRate: 15.0,\n    ),\n  );\n  await room.localParticipant.publishVideoTrack(track);\n} catch (e) {\n  print('could not publish screen sharing: $e');\n}\n```\n\n### End to End Encryption\n\nLiveKit supports end-to-end encryption for audio/video data sent over the network.\nBy default, the native platform can support E2EE without any settings, but for flutter web, you need to use the following steps to create `e2ee.worker.dart.js` file.\n\n```bash\n# for example app\ndart compile js web/e2ee.worker.dart -o example/web/e2ee.worker.dart.js -m\n# for your project\nexport YOU_PROJECT_DIR=your_project_dir\ngit clone https://github.com/livekit/client-sdk-flutter.git\ncd client-sdk-flutter \u0026\u0026 flutter pub get\ndart compile js web/e2ee.worker.dart -o ${YOU_PROJECT_DIR}/web/e2ee.worker.dart.js -m\n```\n\n### Advanced track manipulation\n\nThe setCameraEnabled/setMicrophoneEnabled helpers are wrappers around the Track API.\n\nYou can also manually create and publish tracks:\n\n```dart\nvar localVideo = await LocalVideoTrack.createCameraTrack();\nawait room.localParticipant.publishVideoTrack(localVideo);\n```\n\n### Rendering video\n\nEach track can be rendered separately with the provided `VideoTrackRenderer` widget.\n\n```dart\nVideoTrack? track;\n\n@override\nWidget build(BuildContext context) {\n  if (track != null) {\n    return VideoTrackRenderer(track);\n  } else {\n    return Container(\n      color: Colors.grey,\n    );\n  }\n}\n```\n\n### Audio handling\n\nAudio tracks are played automatically as long as you are subscribed to them.\n\n### Handling changes\n\nLiveKit client makes it simple to build declarative UI that reacts to state changes. It notifies changes in two ways\n\n- `ChangeNotifier` - generic notification of changes. This is useful when you are building reactive UI and only care about changes that may impact rendering.\n- `EventsListener\u003cEvent\u003e` - listener pattern to listen to specific events (see [events.dart](https://github.com/livekit/client-sdk-flutter/blob/main/lib/src/events.dart)).\n\nThis example will show you how to use both to react to room events.\n\n```dart\nclass RoomWidget extends StatefulWidget {\n  final Room room;\n\n  RoomWidget(this.room);\n\n  @override\n  State\u003cStatefulWidget\u003e createState() {\n    return _RoomState();\n  }\n}\n\nclass _RoomState extends State\u003cRoomWidget\u003e {\n  late final EventsListener\u003cRoomEvent\u003e _listener = widget.room.createListener();\n\n  @override\n  void initState() {\n    super.initState();\n    // used for generic change updates\n    widget.room.addListener(_onChange);\n\n    // used for specific events\n    _listener\n      ..on\u003cRoomDisconnectedEvent\u003e((_) {\n        // handle disconnect\n      })\n      ..on\u003cParticipantConnectedEvent\u003e((e) {\n        print(\"participant joined: ${e.participant.identity}\");\n      })\n  }\n\n  @override\n  void dispose() {\n    // be sure to dispose listener to stop listening to further updates\n    _listener.dispose();\n    widget.room.removeListener(_onChange);\n    super.dispose();\n  }\n\n  void _onChange() {\n    // perform computations and then call setState\n    // setState will trigger a build\n    setState(() {\n      // your updates here\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // your build function\n  }\n}\n```\n\nSimilarly, you could do the same when rendering participants. Reacting to changes makes it possible to handle tracks published/unpublished or re-ordering participants in your UI.\n\n```dart\nclass VideoView extends StatefulWidget {\n  final Participant participant;\n\n  VideoView(this.participant);\n\n  @override\n  State\u003cStatefulWidget\u003e createState() {\n    return _VideoViewState();\n  }\n}\n\nclass _VideoViewState extends State\u003cVideoView\u003e {\n  TrackPublication? videoPub;\n\n  @override\n  void initState() {\n    super.initState();\n    widget.participant.addListener(this._onParticipantChanged);\n    // trigger initial change\n    _onParticipantChanged();\n  }\n\n  @override\n  void dispose() {\n    widget.participant.removeListener(this._onParticipantChanged);\n    super.dispose();\n  }\n\n  @override\n  void didUpdateWidget(covariant VideoView oldWidget) {\n    oldWidget.participant.removeListener(_onParticipantChanged);\n    widget.participant.addListener(_onParticipantChanged);\n    _onParticipantChanged();\n    super.didUpdateWidget(oldWidget);\n  }\n\n  void _onParticipantChanged() {\n    var subscribedVideos = widget.participant.videoTracks.values.where((pub) {\n      return pub.kind == TrackType.VIDEO \u0026\u0026\n          !pub.isScreenShare \u0026\u0026\n          pub.subscribed;\n    });\n\n    setState(() {\n      if (subscribedVideos.length \u003e 0) {\n        var videoPub = subscribedVideos.first;\n        // when muted, show placeholder\n        if (!videoPub.muted) {\n          this.videoPub = videoPub;\n          return;\n        }\n      }\n      this.videoPub = null;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var videoPub = this.videoPub;\n    if (videoPub != null) {\n      return VideoTrackRenderer(videoPub.track as VideoTrack);\n    } else {\n      return Container(\n        color: Colors.grey,\n      );\n    }\n  }\n}\n```\n\n### Mute, unmute local tracks\n\nOn `LocalTrackPublication`s, you could control if the track is muted by setting its `muted` property. Changing the mute status will generate an `onTrackMuted` or `onTrack Unmuted` delegate call for the local participant. Other participant will receive the status change as well.\n\n```dart\n// mute track\ntrackPub.muted = true;\n\n// unmute track\ntrackPub.muted = false;\n```\n\n### Subscriber controls\n\nWhen subscribing to remote tracks, the client has precise control over status of its subscriptions. You could subscribe or unsubscribe to a track, change its quality, or disabling the track temporarily.\n\nThese controls are accessible on the `RemoteTrackPublication` object.\n\nFor more info, see [Subscribing to tracks](https://docs.livekit.io/home/client/tracks/subscribe/).\n\n## Getting help / Contributing\n\nPlease join us on [Slack](https://livekit.io/join-slack) to get help from our devs / community members. We welcome your contributions(PRs) and details can be discussed there.\n\n## License\n\nApache License 2.0\n\n## Thanks\n\nA huge thank you to [flutter-webrtc](https://github.com/flutter-webrtc/flutter-webrtc) for making it possible to use WebRTC in Flutter.\n\n\u003c!--BEGIN_REPO_NAV--\u003e\n\u003cbr/\u003e\u003ctable\u003e\n\u003cthead\u003e\u003ctr\u003e\u003cth colspan=\"2\"\u003eLiveKit Ecosystem\u003c/th\u003e\u003c/tr\u003e\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\u003ctd\u003eLiveKit SDKs\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/client-sdk-js\"\u003eBrowser\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-swift\"\u003eiOS/macOS/visionOS\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-android\"\u003eAndroid\u003c/a\u003e · \u003cb\u003eFlutter\u003c/b\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-react-native\"\u003eReact Native\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/rust-sdks\"\u003eRust\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/node-sdks\"\u003eNode.js\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/python-sdks\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-unity\"\u003eUnity\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/client-sdk-unity-web\"\u003eUnity (WebGL)\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eServer APIs\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/node-sdks\"\u003eNode.js\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-go\"\u003eGolang\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-ruby\"\u003eRuby\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/server-sdk-kotlin\"\u003eJava/Kotlin\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/python-sdks\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/rust-sdks\"\u003eRust\u003c/a\u003e · \u003ca href=\"https://github.com/agence104/livekit-server-sdk-php\"\u003ePHP (community)\u003c/a\u003e · \u003ca href=\"https://github.com/pabloFuente/livekit-server-sdk-dotnet\"\u003e.NET (community)\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eUI Components\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/components-js\"\u003eReact\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/components-android\"\u003eAndroid Compose\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/components-swift\"\u003eSwiftUI\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eAgents Frameworks\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/agents\"\u003ePython\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/agents-js\"\u003eNode.js\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/agent-playground\"\u003ePlayground\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eServices\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://github.com/livekit/livekit\"\u003eLiveKit server\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/egress\"\u003eEgress\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/ingress\"\u003eIngress\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/sip\"\u003eSIP\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003c/tr\u003e\n\u003ctr\u003e\u003ctd\u003eResources\u003c/td\u003e\u003ctd\u003e\u003ca href=\"https://docs.livekit.io\"\u003eDocs\u003c/a\u003e · \u003ca href=\"https://github.com/livekit-examples\"\u003eExample apps\u003c/a\u003e · \u003ca href=\"https://livekit.io/cloud\"\u003eCloud\u003c/a\u003e · \u003ca href=\"https://docs.livekit.io/home/self-hosting/deployment\"\u003eSelf-hosting\u003c/a\u003e · \u003ca href=\"https://github.com/livekit/livekit-cli\"\u003eCLI\u003c/a\u003e\u003c/td\u003e\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\u003c!--END_REPO_NAV--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flivekit%2Fclient-sdk-flutter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flivekit%2Fclient-sdk-flutter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flivekit%2Fclient-sdk-flutter/lists"}