{"id":18208,"date":"2025-05-07T11:35:00","date_gmt":"2025-05-07T11:35:00","guid":{"rendered":"https:\/\/www.vdocipher.com\/blog\/?p=18208"},"modified":"2026-01-17T13:03:34","modified_gmt":"2026-01-17T13:03:34","slug":"how-to-build-a-flutter-live-streaming-application","status":"publish","type":"post","link":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/","title":{"rendered":"How to Build a Flutter Live Streaming Application?"},"content":{"rendered":"<p>Live streaming is rapidly becoming an essential feature for modern applications across education, entertainment, and business. Whether you\u2019re hosting live classes, broadcasting sports events, or running webinars, a smooth and interactive streaming experience keeps users engaged.<br \/>\n<br \/>\nIn this guide, we\u2019ll walk you through building a live streaming app in Flutter using VdoCipher\u2019s user-friendly and flexible infrastructure. You\u2019ll learn how to set up the live stream, integrate DVR features, enable interactive chat, and securely embed DRM-protected content, all while ensuring a seamless multi-platform experience.<\/p>\n<div class=\"table-of-content\">\n<div class=\"table-border\">\n<h2 style=\"font-size: 22px; padding-top: 20px;\">Table of Contents:<\/h2>\n<div id=\"table-of-content\" style=\"padding-left: 20px;\">\n<ol>\n<li><a class=\"hashSmoothScroll\" href=\"#1\">Why Choose VdoCipher for Live Streaming?<\/a><\/li>\n<li><a class=\"hashSmoothScroll\" href=\"#2\">Building a Low-Latency Flutter Live Stream App<\/a><\/li>\n<li><a class=\"hashSmoothScroll\" href=\"#3\">Understanding Platform-Specific WebView Parameters<\/a><\/li>\n<li><a class=\"hashSmoothScroll\" href=\"#4\">Secure DRM Video Streaming in Flutter Using the VdoCipher Package<\/a><\/li>\n<li><a class=\"hashSmoothScroll\" href=\"#5\">Conclusion<\/a><\/li>\n<li><a class=\"hashSmoothScroll\" href=\"#6\">FAQs Around Flutter Live Streaming Application<\/a><\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/div>\n<h2 id=\"1\"><strong>Why Choose VdoCipher for Live Streaming?<\/strong><\/h2>\n<p>VdoCipher offers tools to make live streaming flexible, engaging, and friendly. Its features are designed to meet the demands of modern streaming, from education to entertainment.<!-- notionvc: 2592f92e-67ba-49ea-8c11-c6f6ee8a8608 --><\/p>\n<ol>\n<li><strong>DVR for Enhanced Flexibility<\/strong><\/li>\n<\/ol>\n<ul>\n<li><strong>Pause, Rewind, Replay:<\/strong> DVR empowers viewers to control the live stream, making it ideal for educational sessions or long events like webinars and matches.<\/li>\n<li><strong>Catch Up Anytime:<\/strong> Latecomers can rewind and join the stream seamlessly without missing key moments.<\/li>\n<li><strong>Smooth Experience:<\/strong> The DVR buffer (1\u20132 hours) ensures uninterrupted access to past sections while staying connected to live content.<\/li>\n<\/ul>\n<ol start=\"2\">\n<li><strong>Interactive Live Chat<\/strong><\/li>\n<\/ol>\n<ul>\n<li><strong>Real-Time Interaction:<\/strong> Engage your audience with live Q&amp;A, polls, and discussions during streams.<\/li>\n<li><strong>Anonymous Contributions:<\/strong> Viewers can share questions or opinions without revealing their identity.<\/li>\n<\/ul>\n<p><!-- notionvc: 4f0e3643-a852-4ca0-a788-4ec5256b5d0a --><\/p>\n<div class=\"outer-cta\">\n<div class=\"inner-cta\">Explore More \u2705<br \/>\n<a class=\"cta-short-line\" href=\"https:\/\/www.vdocipher.com\/live-streaming-platform\/?utm_source=blog&amp;utm_medium=banner-cta&amp;utm_campaign=drm\">With VdoCipher You Can Stream Your Content Live in 5 minutes<\/a><\/div>\n<div class=\"inner-cta\">\n<p style=\"font-size: 16px !important; font-weight: 400; margin: 0!important;\">VdoCipher live can help you stream your content live to your users. You can also engage with your audience with the chat feature.<\/p>\n<\/div>\n<\/div>\n<h2 id=\"2\"><strong>QuickStart: Building a Low-Latency Flutter Live Stream App<\/strong><\/h2>\n<p>In this tutorial, we&#8217;ll quickly build a low-latency in-app live streaming experience. The live stream will be broadcast using VdoCipher\u2019s global network of servers.<!-- notionvc: 0eedfedf-c686-46e1-bb10-53819ce71322 --><\/p>\n<p><b>You will learn how to:<\/b><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Setting up a live stream from VdoCipher\u2019s dashboard.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Streaming from the OBS software application (or similar tools).<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Streaming live videos in a Flutter application using VdoCipher.<\/li>\n<\/ol>\n<h3><strong>Set Up a Live Stream on VdoCipher\u2019s Dashboard<\/strong><\/h3>\n<p>Open the Live Stream section from the left menu in the VdoCipher dashboard and set up a live stream by pressing <b>Create New Live Stream<\/b>.<\/p>\n<p><b>Note:<\/b> You can also schedule the live stream for later.<\/p>\n<p><a href=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-18212\" src=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-1024x500.png\" alt=\"Live streaming dashboard\" width=\"1024\" height=\"500\" srcset=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-1024x500.png 1024w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-300x147.png 300w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-768x375.png 768w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-1536x751.png 1536w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1-150x73.png 150w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/1.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p>Now enter a title for your live stream for reference and choose preferred chat mode then press the create button to generate streaming credentials for your live stream. Generating stream credentials, this might take up to 90 sec.<\/p>\n<p>Different chat modes and their meaning:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Anonymous\/Non-authenticated Chat: Allow anyone to join in the chats and set their own name<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Authenticated Chat: Allow only viewers who have generated the tokens via logging into your platform, you can keep the same user name as on your platform.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Off : There will be no Chat Option.<\/li>\n<\/ul>\n<h3><strong>Streaming from OBS Studio Software Application<\/strong><!-- notionvc: 683036ab-6415-4fa2-8dca-1489422900b0 --><\/h3>\n<p>Once streaming credentials are generated now you can use these credentials to start a video stream from an OBS Studio or any popular streaming software or Zoom. <br \/>\n<\/p>\n<p><strong>Set Up OBS Studio for Streaming:<\/strong><\/p>\n<ol>\n<li><strong>Setting up OBS studio<\/strong>(or any other streaming software)Install <a href=\"https:\/\/obsproject.com\/download\">OBS studio<\/a> and change the OBS studio settings as mentioned below:\n<ol>\n<li>Click on the \u201cSettings\u201d button, and then go to the \u201cOutput\u201d section. Some settings will look different on Mac vs Windows. But key settings are the same in both systems.<\/li>\n<li>Choose \u201cVideo Bitrate\u201d as 2500 kbps for lecture\/webinar\/course content, if you have other forms of content like events\/sports\/movies, then you can also choose higher bitrates up to 7000 kbps. But 2500 kbps should be quite good for lecture content, and we recommend it.<\/li>\n<li>Choose \u201cAudio Bitrate\u201d as 128.<\/li>\n<li>Please keep the encoder preset and other settings to default settings. Setting \u2192 Output<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p><!-- notionvc: c5705397-c22a-48e6-9fe4-24e4860112b8 --><\/p>\n<p><a href=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-18221\" src=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-1024x441.png\" alt=\"Video Audio bitrate settings for live streaming\" width=\"1024\" height=\"441\" srcset=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-1024x441.png 1024w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-300x129.png 300w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-768x331.png 768w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-1536x661.png 1536w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming-150x65.png 150w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Video-Audio-bitrate-settings-for-live-streaming.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p><b>2.Frame Rate &amp; Resolution: <\/b>Go to the \u201cVideo\u201d section and ensure that \u201cCommon FPS Values\u201d is 30. You can use 720p or 1080p resolution as needed.<\/p>\n<p><a href=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-18222\" src=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-1024x399.png\" alt=\"FPS settings for live streaming\" width=\"1024\" height=\"399\" srcset=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-1024x399.png 1024w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-300x117.png 300w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-768x299.png 768w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-1536x598.png 1536w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming-150x58.png 150w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/FPS-settings-for-live-streaming.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p><b>3. Server URL &amp; Stream Key: <\/b>Now Go to OBS in Settings \u2192 Stream section. Copy and paste the server URL and stream key from the last step and paste them into OBS (Settings \u2192 Stream) respective fields. Click on OK.<\/p>\n<p><a href=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-large wp-image-18223\" src=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-1024x284.png\" alt=\"Server URL and Stream Key\" width=\"1024\" height=\"284\" srcset=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-1024x284.png 1024w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-300x83.png 300w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-768x213.png 768w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-1536x426.png 1536w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key-150x42.png 150w, https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Server-URL-and-Stream-Key.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/p>\n<p><b>4. Add a Source &amp; Start Streaming: <\/b>Now add your video source in the OBS and click on \u201cstart streaming\u201d.<\/p>\n<p>Your content now flows to VdoCipher\u2019s servers and becomes accessible to your Flutter app.<\/p>\n<h3><strong>Stream Live Videos in a Flutter Application Using VdoCipher<\/strong><\/h3>\n<p><strong>Getting Started with Flutter Live Streaming.<\/strong><\/p>\n<p>To embed a live stream in your Flutter app, use the <b>webview_flutter<\/b> plugin in combination with JWT (JSON Web Token) for authentication.<\/p>\n<blockquote><p>\u26a0\ufe0f Security Warning: Token generation must be performed on a secure backend server using your chat secret key. Never expose or use the chatSecret in your Flutter or any client-side code.<\/p><\/blockquote>\n<p>Here&#8217;s a step-by-step guide to implementing live streaming with VdoCipher.<\/p>\n<ol>\n<li><strong>Generate Token Securely on Backend<\/strong><\/li>\n<\/ol>\n<p>Here\u2019s an example using <strong>Node.js (JavaScript)<\/strong> to generate the JWT token securely:<\/p>\n<pre><code class=\"language-jsx\">import * as jwt from 'jsonwebtoken';\r\n\r\nconst chatSecret = 'YOUR_CHAT_SECRET_KEY'; \/\/ Keep this secret and safe\r\n\r\nconst payload = {\r\n  userId: 'uniqueUserId123', \/\/ must be unique for each user\r\n  userInfo: {\r\n    username: 'JohnDoe',     \/\/ name shown in chat\r\n    avatar: '',              \/\/ optional avatar URL\r\n  }\r\n};\r\n\r\nconst token = jwt.sign(payload, chatSecret); \/\/ Send this token to the app\r\n<\/code><\/pre>\n<p>This <code>token<\/code> should be sent from your server to the Flutter app via a secure API endpoint.<\/p>\n<ol>\n<li><strong>Flutter Code (Using Token from Backend)<\/strong><\/li>\n<\/ol>\n<p>Here\u2019s how to set up live streaming in your Flutter app. In this example, assume the token is securely generated on your backend and passed to the Flutter app:<\/p>\n<pre><code class=\"language-dart\">import 'package:flutter\/material.dart';\r\nimport 'package:webview_flutter\/webview_flutter.dart';\r\nimport 'package:webview_flutter_wkwebview\/webview_flutter_wkwebview.dart';\r\n\r\nclass VdoLiveStream extends StatefulWidget {\r\n  const VdoLiveStream({Key? key}) : super(key: key);\r\n\r\n  @override\r\n  State&lt;VdoLiveStream&gt; createState() =&gt; _VdoLiveStreamState();\r\n}\r\n\r\nclass _VdoLiveStreamState extends State&lt;VdoLiveStream&gt; {\r\n  late WebViewController controller;\r\n\r\n  @override\r\n  void initState() {\r\n    super.initState();\r\n\r\n    \/\/ Values retrieved from your backend\r\n    const liveId = 'YOUR_LIVE_ID';\r\n    const token = 'TOKEN_FROM_BACKEND'; \/\/ Secure token received from backend\r\n\r\n    \/\/ Configure WebView parameters\r\n    late final PlatformWebViewControllerCreationParams params;\r\n    if (WebViewPlatform.instance is WebKitWebViewPlatform) {\r\n      params = WebKitWebViewControllerCreationParams(\r\n        allowsInlineMediaPlayback: true,\r\n        mediaTypesRequiringUserAction: const &lt;PlaybackMediaTypes&gt;{},\r\n      );\r\n    } else {\r\n      params = const PlatformWebViewControllerCreationParams();\r\n    }\r\n\r\n    \/\/ Initialize WebView controller\r\n    controller = WebViewController.fromPlatformCreationParams(params)\r\n      ..setJavaScriptMode(JavaScriptMode.unrestricted)\r\n      ..setNavigationDelegate(NavigationDelegate(\r\n        onPageStarted: (url) =&gt; print(\"Loading URL: $url\"),\r\n        onPageFinished: (url) =&gt; print(\"Page Loaded: $url\"),\r\n        onHttpError: (error) =&gt; print(\"HTTP Error: ${error.description}\"),\r\n        onWebResourceError: (error) =&gt; print(\"Resource Error: ${error.description}\"),\r\n      ))\r\n      ..loadRequest(Uri.parse(\r\n          '&lt;https:\/\/player.vdocipher.com\/live?liveId=$liveId&amp;token=$token&gt;'));\r\n  }\r\n\r\n  @override\r\n  Widget build(BuildContext context) {\r\n    return Scaffold(body: WebViewWidget(controller: controller));\r\n  }\r\n}\r\n<\/code><\/pre>\n<h3>Key Points<\/h3>\n<ol>\n<li><strong>Authentication with JWT (If Chat Is Enabled)<\/strong>A JSON Web Token (JWT) is used for secure user authentication and is only required if the chat feature is enabled in your live stream.\u26a0\ufe0f <strong>Important:<\/strong> This token <strong>must be generated securely on your backend<\/strong> using your <strong>chat secret<\/strong>. Never include the secret or generate the token on the client side.<\/li>\n<li><strong>Embedding the Live Stream<\/strong>The WebView widget loads the VdoCipher live stream URL, which includes the <strong>liveId<\/strong>. If the chat feature is enabled, a token generated using JWT is also appended to the URL.<\/li>\n<li><strong>Platform-Specific WebView Configuration<\/strong>The code is compatible with both Android and iOS platforms, ensuring seamless functionality, including inline media playback.<\/li>\n<\/ol>\n<p><!-- notionvc: 3de47d72-f743-46e4-a20c-33b2d7448d96 --><\/p>\n<h3><strong>Steps to Implement<\/strong><\/h3>\n<p><b>1. Install Required Packages<\/b><\/p>\n<p>Add the following packages to your pubspec.yaml file:<\/p>\n<p>dependencies:<br \/>\nflutter:<br \/>\nsdk: flutter<br \/>\nwebview_flutter: ^4.10.0<br \/>\nwebview_flutter_wkwebview: ^3.16.3<\/p>\n<p><b style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\">2. Replace Placeholders<\/b> Update chatSecret and liveId with the values provided by VdoCipher.<\/p>\n<p><b> <\/b><b>3. Run the App<\/b><b> <\/b>Use flutter run to test the live stream functionality. <\/p>\n<h2 id=\"3\"><strong>Understanding Platform-Specific WebView Parameters<\/strong><\/h2>\n<p>When using WebView in Flutter, media playback is handled differently on iOS and Android. To ensure smooth functionality across platforms, specific configurations may be required based on the target platform. <br \/>\n<br \/>\nHere&#8217;s how the code addresses these differences:<\/p>\n<pre>if (WebViewPlatform.instance is WebKitWebViewPlatform) {\r\n  params = WebKitWebViewControllerCreationParams(\r\n    allowsInlineMediaPlayback: true,\r\n    mediaTypesRequiringUserAction: const {},\r\n  );\r\n} else {\r\n  params = const PlatformWebViewControllerCreationParams();\r\n}\r\n\r\n<\/pre>\n<h3><strong>What Does This Code Do?<\/strong><\/h3>\n<p>This snippet checks whether the current platform uses WebKit, the web engine for iOS. If WebKit is detected, specific settings tailored for iOS are applied. Otherwise, it defaults to the standard settings, ensuring compatibility for Android.<\/p>\n<p><b>1. For iOS (WebKit)<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>allowsInlineMediaPlayback: true<\/b><b><br \/>\n<\/b><span style=\"font-weight: 400;\">This enables videos to play directly within the WebView (inline) instead of switching to fullscreen mode. It\u2019s particularly useful for maintaining the video as part of your app\u2019s interface.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>mediaTypesRequiringUserAction: const &lt;PlaybackMediaTypes&gt;{}<\/b><b><br \/>\n<\/b>By default, iOS requires the user to tap a play button before videos or audio can start. This setting removes that restriction, allowing the video to play automatically.<\/li>\n<\/ul>\n<p><b>2. For Other Platforms (like Android)<\/b><\/p>\n<p>If the app is running on a platform other than iOS, the code uses the generic <b>PlatformWebViewControllerCreationParams<\/b>. No additional configurations are applied, as they are not required for other platforms.<\/p>\n<h3><strong>Why Do You Need This?<\/strong><\/h3>\n<p>WebView behaves differently on iOS and Android, especially when handling videos:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">On iOS, videos don\u2019t play inline by default and may require user interaction. These parameters address that issue, making the behavior more user-friendly by allowing videos to play automatically within the WebView.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">On Android, these settings aren\u2019t necessary because inline playback and autoplay are already supported by default, without requiring additional configuration.<\/li>\n<\/ul>\n<h3><strong>When Should You Change These?<\/strong><\/h3>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">If you want videos to always play inline, keep the <strong>allowsInlineMediaPlayback<\/strong> setting set to true.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">If you want users to manually tap play, add the required media types to <strong>mediaTypesRequiringUserAction<\/strong>.<\/li>\n<\/ul>\n<h2 id=\"4\"><strong>Secure DRM Video Streaming in Flutter Using the VdoCipher Package<\/strong><\/h2>\n<p>VdoCipher is a platform designed to provide DRM protection and secure video hosting. It enables developers to easily integrate and stream protected video content. To further simplify this process for Flutter developers, VdoCipher offers the vdocipher_flutter package, a tool that allows the integration of DRM-protected video streaming into Flutter applications.<\/p>\n<p>Here, we\u2019ll explore:<\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Key features of the <strong>vdocipher_flutter<\/strong> package.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">How to set it up in a Flutter project.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Step-by-step instructions to stream DRM-protected videos.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\">Best practices for secure and efficient video playback.<\/li>\n<\/ul>\n<h3><strong>Key features of the vdocipher_flutter Package<\/strong><\/h3>\n<p>The <strong>vdocipher_flutter<\/strong> package enables developers to securely integrate and stream DRM-protected videos within Flutter applications. It ensures smooth video playback while maintaining content protection in your Flutter app.<\/p>\n<p><b>Features<\/b><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>DRM Protection<\/b><b><br \/>\n<\/b>Streams videos with Widevine and Fairplay DRM, protecting content from screen recording and piracy.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Cross-Platform Support<\/b><b><br \/>\n<\/b>It works seamlessly on Android, iOS, and the web, providing a consistent experience across all devices.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Secure OTP-Based Authentication<\/b><b><br \/>\n<\/b>Videos are played using a secure OTP and playbackInfo, ensuring that only authorized users can access the content.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Inbuilt Player UI<\/b><b><br \/>\n<\/b>The <strong>vdocipher_flutter<\/strong> package provides built-in controls for video playback, including play\/pause, a seekbar, fullscreen toggle, playback speed, subtitles, and chapters. These controls create a smoother user experience. This ready to use implementation saves time and eliminates the need for custom setup.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Offline Playback<\/b><b><br \/>\n<\/b>Supports secure video <a href=\"https:\/\/www.vdocipher.com\/docs\/mobile\/flutter\/offline\/\" target=\"_blank\" rel=\"noopener\">offline downloads<\/a> for offline viewing.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Background Playback<\/b><b><br \/>\n<\/b>Keeps videos playing even when the app is minimized or in the background, and allows controlling media playback through the notification controller.<\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>APIs to Monitor and Control Media Playback<\/b><b><br \/>\n<\/b>Provides APIs to control media playback, track playback status, and monitor events such as play\/pause, current position, seeking, buffering, errors, and more.<\/li>\n<\/ul>\n<h3><strong>How to Set It Up in a Flutter Project<\/strong><\/h3>\n<p>Setting up the <b>vdocipher_flutter<\/b> package in your Flutter project is simple. Follow these steps to get started:<\/p>\n<p>1. Run this command with Flutter:<\/p>\n<pre>$ flutter pub add vdocipher_flutter<\/pre>\n<p>This will add a line like this to your package&#8217;s <strong>pubspec.yaml<\/strong> (and run an implicit <strong>dart pub get<\/strong>)<\/p>\n<pre>dependencies:\r\n  vdocipher_flutter: ^2.7.\r\n<\/pre>\n<p>2. In android section use <a href=\"https:\/\/github.com\/VdoCipher\/sample_flutter_app\/blob\/473ec16d6b4bbe0b1f40afc9780cfb4aee0a0e76\/android\/app\/src\/main\/res\/values\/styles.xml#L18\">Theme.AppCompat theme<\/a> with <strong>MainActivity<\/strong>:<br \/>\nMake this change in <strong>android\/app\/src\/main\/res\/values\/styles.xml<\/strong>.<\/p>\n<pre>&lt;style name=\"NormalTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\"&gt;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0....\r\n&lt;\/style&gt;<\/pre>\n<blockquote><p>\ud83d\udca1There can be multiple styles.xml files depending on the project structure. Please make the above change in all of them, such as <strong>values-night\/styles.xml<\/strong> and <strong>values\/styles.xml<\/strong>.<\/p><\/blockquote>\n<p>3. In android section <a href=\"https:\/\/github.com\/VdoCipher\/sample_flutter_app\/blob\/473ec16d6b4bbe0b1f40afc9780cfb4aee0a0e76\/android\/app\/src\/main\/java\/com\/vdocipher\/sample_flutter_app\/MainActivity.java#L5\">extend the FlutterFragmentActivity class in your MainActivity<\/a> file:<\/p>\n<p>Make this change in <strong>android\/app\/src\/main\/java\/com\/project-name\/MainActivity.java<\/strong>.<\/p>\n<pre>class MainActivity extends FlutterFragmentActivity {\r\n}\r\n<\/pre>\n<blockquote><p>\ud83d\udca1Without using <a href=\"https:\/\/github.com\/VdoCipher\/sample_flutter_app\/blob\/473ec16d6b4bbe0b1f40afc9780cfb4aee0a0e76\/android\/app\/src\/main\/res\/values\/styles.xml#L18\">Theme.AppCompat theme<\/a> with MainActivity.java and <a href=\"https:\/\/github.com\/VdoCipher\/sample_flutter_app\/blob\/473ec16d6b4bbe0b1f40afc9780cfb4aee0a0e76\/android\/app\/src\/main\/java\/com\/vdocipher\/sample_flutter_app\/MainActivity.java#L5\">extending from FlutterFragmentActivity<\/a>, as mentioned in the 2nd and 3rd steps, our plugin will throw exception. For a better understanding, please take a look at our <a href=\"https:\/\/github.com\/VdoCipher\/sample_flutter_app\">sample app<\/a>.<\/p><\/blockquote>\n<h3><strong>Step-by-Step Instructions to Stream DRM-Protected Videos<\/strong><\/h3>\n<p>Streaming DRM-protected videos using the <b>vdocipher_flutter<\/b> package requires a few steps. Here\u2019s a complete guide to help you get started:<\/p>\n<p><b>1. Fetch OTP and Playback Info<br \/>\n<\/b><b><\/b><b> \u2022 <\/b>To retrieve the OTP and playbackInfo for a video, you need to call the authenticated API endpoint: <strong>https:\/\/dev.vdocipher.com\/api\/videos\/{videoID}\/otp<\/strong>. For more details on authentication and usage, please refer to the documentation<a href=\"https:\/\/www.vdocipher.com\/docs\/server\/playbackauth\/otp\"> here<\/a>.<br \/>\n<br \/>\n<b>\u2022 <\/b>The API will return the OTP and playbackInfo required to securely stream the video. Example response:<br \/>\n<\/p>\n<pre>{\r\n\u00a0\u00a0\"otp\": \"your_video_otp\",\r\n\u00a0\u00a0\"playbackInfo\": \"your_video_playback_info\"\r\n}<\/pre>\n<p><b>2. Create <\/b><b>EmbedInfo<\/b><b> Object<br \/>\n<\/b>Once you have the OTP and playbackInfo, you can create an <b>EmbedInfo<\/b> object to initialize the player. This object includes the OTP, playbackInfo, and additional player options such as resume position, autoplay, caption language, and more. For more details, check the documentation <a href=\"https:\/\/pub.dev\/documentation\/vdocipher_flutter\/latest\/vdocipher_flutter\/EmbedInfo-class.html\">here<\/a>.<br \/>\n<br \/>\nHere\u2019s an example of how to create an <b>EmbedInfo<\/b> object. Replace the otp and playbackInfo values with the ones you fetched from the API:<\/p>\n<pre>const EmbedInfo embedInfo = EmbedInfo.streaming(\r\n\u00a0\u00a0otp: 'your_video_otp',\r\n\u00a0\u00a0playbackInfo: 'your_video_playback_info',\r\n\u00a0\u00a0embedInfoOptions: EmbedInfoOptions(\r\n\u00a0\u00a0\u00a0\u00a0preferredCaptionsLanguage: 'en',\r\n\u00a0\u00a0\u00a0\u00a0autoplay: true,\r\n\u00a0\u00a0),\r\n);\r\n\r\n<\/pre>\n<p><b>3. Add the <\/b><b>VdoPlayer<\/b><b> Widget<\/b><b><br \/>\n<\/b>Now, integrate the VdoPlayer widget into your app. Below is a sample implementation that handles various player events, such as errors, fullscreen changes, and picture-in-picture mode changes.<\/p>\n<pre>class VdoPlaybackViewState extends State&lt;VdoPlaybackView&gt; {\r\n\u00a0\u00a0VdoPlayerController? _controller;\r\n\u00a0\u00a0VdoPlayerValue? vdoPlayerValue;\r\n\u00a0\u00a0final double aspectRatio = 16 \/ 9;\r\n\u00a0\u00a0final ValueNotifier&lt;bool&gt; _isFullScreen = ValueNotifier(false);\r\n\u00a0\u00a0Duration? duration;\r\n\r\n\u00a0\u00a0@override\r\n  Widget build(BuildContext context) {\r\n\u00a0\u00a0\u00a0\u00a0const EmbedInfo embedInfo = EmbedInfo.streaming(\r\n\u00a0 otp: 'your_video_otp',\r\n\u00a0 playbackInfo: 'your_video_playback_info',\r\n\u00a0 embedInfoOptions: EmbedInfoOptions(\r\n\u00a0 \u00a0 preferredCaptionsLanguage: 'en',\r\n\u00a0 \u00a0 autoplay: true,\r\n\u00a0 ),\r\n);\r\n return Scaffold(\r\n  body: SafeArea(\r\n  child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [\r\n     Stack(\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 alignment: Alignment.center,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 children: [\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 SizedBox(\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 width: _getPlayerWidth(),\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 height: _getPlayerHeight(),\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 child: VdoPlayer(\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 embedInfo: embedInfo,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 aspectRatio: aspectRatio,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 onError: _onVdoError,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 onFullscreenChange: _onFullscreenChange,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 onPlayerCreated: _onPlayerCreated,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 onPictureInPictureModeChanged: _onPictureInPictureModeChanged,\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ),\r\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ],\r\n\u00a0 \u00a0 \u00a0 \u00a0 ),\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0]),\r\n\u00a0\u00a0\u00a0\u00a0));\r\n\u00a0\u00a0}\r\n\u00a0\u00a0_onVdoError(VdoError vdoError) {\r\n\u00a0\u00a0\u00a0\u00a0if (kDebugMode) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0print(\"Oops, the system encountered a problem: ${vdoError.message}\");\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_onPlayerCreated(VdoPlayerController? controller) {\r\n\u00a0\u00a0\u00a0\u00a0setState(() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_controller = controller;\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_onEventChange(_controller);\r\n\u00a0\u00a0\u00a0\u00a0});\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_onPictureInPictureModeChanged(bool isInPictureInPictureMode) {\r\n\u00a0\u00a0\u00a0\u00a0\/\/ Handle picture-in-picture mode changes\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_onEventChange(VdoPlayerController? controller) {\r\n\u00a0\u00a0\u00a0\u00a0controller?.addListener(() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setState(() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0vdoPlayerValue = controller.value;\u00a0 \/\/ Update player state\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\r\n\u00a0\u00a0\u00a0\u00a0});\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_onFullscreenChange(bool isFullscreen) {\r\n\u00a0\u00a0\u00a0\u00a0setState(() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0_isFullScreen.value = isFullscreen;\u00a0 \/\/ Update full-screen state\r\n\u00a0\u00a0\u00a0\u00a0});\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_getPlayerWidth() {\r\n\u00a0\u00a0\u00a0\u00a0return MediaQuery.of(context).size.width;\u00a0 \/\/ Set player width\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0_getPlayerHeight() {\r\n\u00a0\u00a0\u00a0\u00a0return _isFullScreen.value\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0? MediaQuery.of(context).size.height\u00a0 \/\/ Adjust height for full-screen\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0: _getHeightForWidth(MediaQuery.of(context).size.width);\u00a0 \/\/ Maintain aspect ratio\r\n\u00a0\u00a0}\r\n\r\n\u00a0\u00a0double _getHeightForWidth(double width) {\r\n\u00a0\u00a0\u00a0\u00a0return width \/ aspectRatio;\u00a0 \/\/ Maintain aspect ratio\r\n\u00a0\u00a0}\r\n}\r\n\r\n<\/pre>\n<p><b>Explanation of Functions:<\/b><\/p>\n<ol>\n<li><b>_onVdoError<\/b>: This function is triggered when there\u2019s an error in the video player. It logs the error message to the console during development.<\/li>\n<li><b>_onPlayerCreated<\/b>: Called when the player is successfully created. The player controller is saved and linked to player state changes.<\/li>\n<li><b>_onPictureInPictureModeChanged<\/b>: This function gets triggered when the picture-in-picture mode is toggled. You can implement logic for handling this mode here.<\/li>\n<li><b>_onEventChange<\/b>: Listens for changes in the player state (e.g., playback progress, buffering).<\/li>\n<li><b>_onFullscreenChange<\/b>: This function updates the full screen mode state and ensures the layout is adjusted accordingly.<\/li>\n<li><b>_getPlayerWidth<\/b>: Determines the width of the player, adjusting it based on whether the platform is web or mobile.<\/li>\n<li><b>_getPlayerHeight<\/b>: Adjusts the height of the player depending on whether full-screen mode is active. If not, it uses the aspect ratio to calculate the correct height.<\/li>\n<li><b>_getHeightForWidth<\/b>: Calculates the height of the player based on the provided width, maintaining the aspect ratio (16:9 in this case).<\/li>\n<\/ol>\n<p><b>4. Handle Player Events<\/b><b><br \/>\n<\/b>To listen for playback events and update the player\u2019s state, you can add a listener to the <strong>VdoPlayerController<\/strong>. The following code snippet demonstrates how to track the player\u2019s state and capture changes:<\/p>\n<pre>_onEventChange(VdoPlayerController? controller) {\r\n\u00a0\u00a0controller!.addListener(() {\r\n\u00a0\u00a0\u00a0\u00a0VdoPlayerValue value = controller.value;\r\n\u00a0\u00a0\u00a0\u00a0setState(() {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0vdoPlayerValue = value;\r\n\u00a0\u00a0\u00a0\u00a0});\r\n\u00a0\u00a0});\r\n}<\/pre>\n<p>This listener listens to changes in the <strong>VdoPlayerController<\/strong> and updates the state of your widget with the latest <strong>VdoPlayerValue<\/strong>. The <strong>VdoPlayerValue<\/strong> class contains various properties that describe the current state of the player.<br \/>\n<br \/>\n<b>Key Properties of VdoPlayerValue<br \/>\n<\/b>The <strong>VdoPlayerValue<\/strong> class provides essential information about the player&#8217;s state. Below are the properties you can monitor:<\/p>\n<ul>\n<li><b> bufferedPosition \u2192 Duration<\/b><b><br \/>\n<\/b>Represents the position up to where the video has been buffered. It indicates how much of the video is available for playback without interruptions.<\/li>\n<li><b> duration \u2192 Duration<\/b><b><br \/>\n<\/b>The total duration of the video, from start to finish.<\/li>\n<li><b> isAdaptive \u2192 bool<\/b><b><br \/>\n<\/b>Indicates whether track selection is done in adaptive mode (true) or if a specific track has been selected explicitly (false).<\/li>\n<li><b> isBuffering \u2192 bool<\/b><b><br \/>\n<\/b>True if the video is currently buffering, meaning data is being loaded before playback can continue.<\/li>\n<li><b><\/b> <b>isEnded \u2192 bool<\/b><b><br \/>\n<\/b>True if the currently loaded video has reached its end and playback is complete.<\/li>\n<li><b> isLoading \u2192 bool<\/b><b><br \/>\n<\/b>True if a video is currently being loaded into the player, meaning data for playback is being fetched.<\/li>\n<li><b> isMuted \u2192 bool<\/b><b><br \/>\n<\/b>Specifies whether the audio output of the video is muted. This is supported only on the web and is always false for Android and iOS platforms.<\/li>\n<li><b> isPlaying \u2192 bool<\/b><b><br \/>\n<\/b>True if the video is currently playing, or false if it is paused.<\/li>\n<li><b> mediaInfo \u2192 MediaInfo?<\/b><b><br \/>\n<\/b>Provides details related to the currently loaded video, such as metadata or additional information.<\/li>\n<li><b> playbackSpeed \u2192 double<\/b><b><br \/>\n<\/b>Represents the current playback speed of the video. A value of 1.0 typically indicates normal speed.<\/li>\n<li><b> playbackSpeedOptions \u2192 List&lt;double&gt;<\/b><b><br \/>\n<\/b>A list of available playback speed options that the user can select from during playback.<\/li>\n<li><b> position \u2192 Duration<\/b><b><br \/>\n<\/b>The current playback position of the video, indicating where the video is currently being played from.<\/li>\n<li><b> resizeMode \u2192 ResizeMode<\/b><b><br \/>\n<\/b>Specifies how the video should be resized to fit within the player, such as stretching, scaling, or fitting the aspect ratio.<\/li>\n<li><b> skipDuration \u2192 Duration<\/b><b><br \/>\n<\/b>The duration for skipping backward or forward in the video, allowing users to jump a specific amount of time in the playback.<\/li>\n<li><b> subtitleTrack \u2192 SubtitleTrack?<\/b><b><br \/>\n<\/b>Represents the currently selected subtitle track. Returns null if subtitles are disabled.<\/li>\n<li><b> subtitleTracks \u2192 List&lt;SubtitleTrack&gt;<\/b><b><br \/>\n<\/b>A list of all available subtitle tracks for the video, allowing users to select between them if needed.<\/li>\n<li><b> vdoError \u2192 VdoError?<\/b><b><br \/>\n<\/b>Provides details about any error encountered during video loading or playback. If null, no error has occurred.<\/li>\n<li><b> videoTrack \u2192 VideoTrack?<\/b><b><br \/>\n<\/b>The currently selected video track. This may refer to the video quality or resolution.<\/li>\n<li><b> videoTracks \u2192 List&lt;VideoTrack&gt;<\/b><b><br \/>\n<\/b>A list of all available video tracks, allowing the user to choose among different video qualities or resolutions.<\/li>\n<li><b> volume \u2192 double?<\/b><b><br \/>\n<\/b>Specifies the current volume level of the audio\/video, represented as a value between 0.0 (muted) and 1.0 (maximum volume). This is supported only on the web and is always null for Android and iOS platforms.<br \/>\nFor instance, to check if the video is playing and update the UI accordingly, you can use:<\/li>\n<\/ul>\n<pre>if (vdoPlayerValue.isPlaying) {\r\n\u00a0\u00a0\/\/ Update UI to show that the video is playing\r\n} else {\r\n\u00a0\u00a0\/\/ Show the pause or stopped state\r\n}<\/pre>\n<h3><strong>Handling Player State Changes<\/strong><\/h3>\n<p>You can use the properties from VdoPlayerValue to implement dynamic behavior based on the playback state:<\/p>\n<ol>\n<li><b> Buffering Handling<\/b>: Use <strong>isBufferin<\/strong>g to show a loading spinner while the video is buffering.<\/li>\n<li><b> Tracking Progress<\/b>: Use <strong>position<\/strong> and <strong>duration<\/strong> to show a real time progress bar to the user.<\/li>\n<li><b> Playback Speed<\/b>: You can adjust playback speed by using the <strong>playbackSpeed<\/strong> property and providing speed options using <strong>playbackSpeedOptions<\/strong>.<\/li>\n<li><b> Error Handling<\/b>: If a playback error occurs, <strong>vdoError<\/strong> will provide the error details, which can be displayed to the user.<\/li>\n<\/ol>\n<p>Here\u2019s how you can react to these events:<\/p>\n<pre>if (vdoPlayerValue.isBuffering) {\r\n\u00a0\u00a0print(\"Buffering...\");\r\n} else {\r\n\u00a0\u00a0print(\"Buffering complete.\");\r\n}\r\nif (vdoPlayerValue.isEnded) {\r\n\u00a0\u00a0print(\"Playback ended.\");\r\n}\r\nif (vdoPlayerValue.vdoError != null) {\r\n\u00a0\u00a0print(\"Error: ${vdoPlayerValue.vdoError?.message}\");\r\n}\r\n\r\n<b style=\"font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;\">5. Offline download and Playback<\/b><\/pre>\n<p>The VdoCipher Flutter package offers the capability to download videos to local storage for offline playback on Android devices running Lollipop (API level 21) and above.<br \/>\n<br \/>\nIt includes APIs to:<br \/>\n\u2022 Fetch available download options for a video in your dashboard<br \/>\n\u2022 Download media assets to local storage<br \/>\n\u2022 Track download progress<br \/>\n\u2022 Manage downloads (query, stop, resume or delete downloads)<\/p>\n<p>For detailed instructions, refer to the<a href=\"https:\/\/www.vdocipher.com\/docs\/mobile\/flutter\/offline\"> complete documentation<\/a>.<\/p>\n<p><b>6. Background playback (Android Only)<\/b><\/p>\n<p>Background playback allows the media to continue playing seamlessly while multitasking or switching apps. Starting from version 2.7.0, the <strong>vdocipher_flutter<\/strong> package supports uninterrupted playback, even when the screen is locked or the app is exited.<br \/>\n<br \/>\nThe <b>vdocipher_flutter<\/b> package offers three playback modes, each providing a different level of persistence:<br \/>\n<br \/>\n\u2022 <b>Default Mode<\/b>: The media will immediately pause when the user closes or navigates away from the player screen.<br \/>\n\u2022 <b>Continue playback on back press:<\/b> Playback pauses when the app is completely exited, but continues if the app is minimized or the back button is pressed.<br \/>\n\u2022 <b>Continue playback on app exit:<\/b> The media continues playing in the background, regardless of whether the app is minimized or fully exited.<\/p>\n<p>For a more detailed understanding on different playback modes and how to implement them, refer to the<a href=\"https:\/\/www.vdocipher.com\/docs\/mobile\/flutter\/background-playback\"> complete documentation<\/a>.<br \/>\n<\/p>\n<div class=\"outer-cta\">\n<div class=\"inner-cta\">\n<p class=\"cta-emoji-line\">Secure Your Content \u2705<\/p>\n<p><a class=\"cta-short-line\" href=\"https:\/\/www.vdocipher.com\/developer\/?utm_source=blog&amp;utm_medium=text-cta&amp;utm_campaign=features\">Smooth playback and top-notch security for your videos<\/a><\/p>\n<\/div>\n<div class=\"inner-cta\">\n<p style=\"font-size: 16px !important; font-weight: 400; margin: 0!important;\">Protect Your Videos from Unauthorised Downloads &amp; Screen Capture with VdoCipher&#8217;s Flutter SDK<\/p>\n<\/div>\n<\/div>\n<h2 id=\"5\"><strong>Conclusion<\/strong><\/h2>\n<p>Building a Flutter live streaming app with VdoCipher gives you a secure, flexible, and feature-rich platform. From setting up DVR and live chat to embedding DRM-protected content with a user-friendly interface, VdoCipher empowers developers to deliver high-quality, engaging live sessions. Along with the live streaming platform, you can also use other <a href=\"https:\/\/www.vdocipher.com\/blog\/live-streaming-tools\/\" target=\"_blank\" rel=\"noopener\">live streaming tools<\/a> to enhance the overall live experiences.<br \/>\n<br \/>\nWith the steps outlined above, you can confidently create a smooth, interactive, and secure live streaming solution that meets the demands of today\u2019s audiences.<\/p>\n<h3>Ready to Get Started?<\/h3>\n<p>Use <a href=\"https:\/\/www.vdocipher.com\/live-streaming-platform\/\" target=\"_blank\" rel=\"noopener\">ZenStream<\/a> by VdoCipher to set up your Flutter environment, and start streaming confidently. Your users will appreciate the secure, high-quality, and interactive video experience you provide.<\/p>\n<h2 id=\"6\"><strong>FAQs Around Flutter Live Streaming<\/strong><\/h2>\n<h3>1. How do I start a live stream using VdoCipher in my Flutter app?<\/h3>\n<p>You\u2019ll need to create a live stream via the VdoCipher dashboard, obtain the credentials, and then integrate them with your Flutter WebView to start streaming.<\/p>\n<h3>2. Can I enable DVR features, like pausing and rewinding, during a live stream?<\/h3>\n<p>Yes. By integrating DVR, viewers can pause, rewind, and replay live sessions without missing any content.<\/p>\n<h3>3. How do I set up and secure live chat within the stream?<\/h3>\n<p>You can enable chat modes directly from VdoCipher\u2019s dashboard, and use JWT authentication tokens to ensure only authorized users participate.<\/p>\n<h3>4. What steps can I take to ensure low latency and smooth playback on multiple devices?<\/h3>\n<p>Follow the recommended encoder settings, use adaptive bitrate streaming, and configure your Flutter WebView for optimal platform-specific playback settings.<\/p>\n<p>\u00a0<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Live streaming is rapidly becoming an essential feature for modern applications across education, entertainment, and business. Whether you\u2019re hosting live classes, broadcasting sports events, or running webinars, a smooth and interactive streaming experience keeps users engaged. In this guide, we\u2019ll walk you through building a live streaming app in Flutter using VdoCipher\u2019s user-friendly and flexible [&hellip;]<\/p>\n","protected":false},"author":30,"featured_media":18214,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[341],"tags":[355,342],"class_list":{"0":"post-18208","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-live-streaming","8":"tag-flutter","9":"tag-live-streaming","10":"entry"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.0 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Developing a Low-Latency, Fully-Featured Flutter Live Streaming App<\/title>\n<meta name=\"description\" content=\"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Build a Flutter Live Streaming Application?\" \/>\n<meta property=\"og:description\" content=\"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\" \/>\n<meta property=\"og:site_name\" content=\"VdoCipher Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/vdociphertech\/\" \/>\n<meta property=\"article:published_time\" content=\"2025-05-07T11:35:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-17T13:03:34+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"450\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Nafis\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@vdocipher\" \/>\n<meta name=\"twitter:site\" content=\"@vdocipher\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nafis\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\"},\"author\":{\"name\":\"Nafis\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/d8a67133aa1086f059d8d142799b444d\"},\"headline\":\"How to Build a Flutter Live Streaming Application?\",\"datePublished\":\"2025-05-07T11:35:00+00:00\",\"dateModified\":\"2026-01-17T13:03:34+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\"},\"wordCount\":3246,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png\",\"keywords\":[\"flutter\",\"live streaming\"],\"articleSection\":[\"Live Streaming\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\",\"url\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\",\"name\":\"Developing a Low-Latency, Fully-Featured Flutter Live Streaming App\",\"isPartOf\":{\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png\",\"datePublished\":\"2025-05-07T11:35:00+00:00\",\"dateModified\":\"2026-01-17T13:03:34+00:00\",\"description\":\"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage\",\"url\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png\",\"contentUrl\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png\",\"width\":1000,\"height\":450,\"caption\":\"How to build a Flutter live streaming app\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www-uat.vdocipher.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Build a Flutter Live Streaming Application?\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#website\",\"url\":\"https:\/\/www-uat.vdocipher.com\/blog\/\",\"name\":\"VdoCipher Blog\",\"description\":\"Secure Video Streaming\",\"publisher\":{\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www-uat.vdocipher.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#organization\",\"name\":\"VdoCipher\",\"url\":\"https:\/\/www-uat.vdocipher.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2016\/11\/VdoCipher-logo2.png\",\"contentUrl\":\"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2016\/11\/VdoCipher-logo2.png\",\"width\":1625,\"height\":1925,\"caption\":\"VdoCipher\"},\"image\":{\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/vdociphertech\/\",\"https:\/\/x.com\/vdocipher\",\"https:\/\/www.linkedin.com\/company\/vdocipher\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/d8a67133aa1086f059d8d142799b444d\",\"name\":\"Nafis\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/b931184ba3509fc35dfe89608c0922255ce50b3fcb9752d5dde23523e29ce3e4?s=96&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/b931184ba3509fc35dfe89608c0922255ce50b3fcb9752d5dde23523e29ce3e4?s=96&r=g\",\"caption\":\"Nafis\"},\"description\":\"Nafis is a skilled Android developer with a passion for building intuitive and high-performance mobile applications\",\"url\":\"https:\/\/www.vdocipher.com\/blog\/author\/nafis\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Developing a Low-Latency, Fully-Featured Flutter Live Streaming App","description":"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/","og_locale":"en_US","og_type":"article","og_title":"How to Build a Flutter Live Streaming Application?","og_description":"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.","og_url":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/","og_site_name":"VdoCipher Blog","article_publisher":"https:\/\/www.facebook.com\/vdociphertech\/","article_published_time":"2025-05-07T11:35:00+00:00","article_modified_time":"2026-01-17T13:03:34+00:00","og_image":[{"width":1000,"height":450,"url":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png","type":"image\/png"}],"author":"Nafis","twitter_card":"summary_large_image","twitter_creator":"@vdocipher","twitter_site":"@vdocipher","twitter_misc":{"Written by":"Nafis","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#article","isPartOf":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/"},"author":{"name":"Nafis","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/d8a67133aa1086f059d8d142799b444d"},"headline":"How to Build a Flutter Live Streaming Application?","datePublished":"2025-05-07T11:35:00+00:00","dateModified":"2026-01-17T13:03:34+00:00","mainEntityOfPage":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/"},"wordCount":3246,"commentCount":0,"publisher":{"@id":"https:\/\/www-uat.vdocipher.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage"},"thumbnailUrl":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png","keywords":["flutter","live streaming"],"articleSection":["Live Streaming"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/","url":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/","name":"Developing a Low-Latency, Fully-Featured Flutter Live Streaming App","isPartOf":{"@id":"https:\/\/www-uat.vdocipher.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage"},"image":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage"},"thumbnailUrl":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png","datePublished":"2025-05-07T11:35:00+00:00","dateModified":"2026-01-17T13:03:34+00:00","description":"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.","breadcrumb":{"@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#primaryimage","url":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png","contentUrl":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2024\/12\/Flutter-Live-Streaming-Appplication.png","width":1000,"height":450,"caption":"How to build a Flutter live streaming app"},{"@type":"BreadcrumbList","@id":"https:\/\/www.vdocipher.com\/blog\/flutter-live-streaming-application\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www-uat.vdocipher.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Build a Flutter Live Streaming Application?"}]},{"@type":"WebSite","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#website","url":"https:\/\/www-uat.vdocipher.com\/blog\/","name":"VdoCipher Blog","description":"Secure Video Streaming","publisher":{"@id":"https:\/\/www-uat.vdocipher.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www-uat.vdocipher.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#organization","name":"VdoCipher","url":"https:\/\/www-uat.vdocipher.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2016\/11\/VdoCipher-logo2.png","contentUrl":"https:\/\/www.vdocipher.com\/blog\/wp-content\/uploads\/2016\/11\/VdoCipher-logo2.png","width":1625,"height":1925,"caption":"VdoCipher"},"image":{"@id":"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/vdociphertech\/","https:\/\/x.com\/vdocipher","https:\/\/www.linkedin.com\/company\/vdocipher"]},{"@type":"Person","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/d8a67133aa1086f059d8d142799b444d","name":"Nafis","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www-uat.vdocipher.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/b931184ba3509fc35dfe89608c0922255ce50b3fcb9752d5dde23523e29ce3e4?s=96&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/b931184ba3509fc35dfe89608c0922255ce50b3fcb9752d5dde23523e29ce3e4?s=96&r=g","caption":"Nafis"},"description":"Nafis is a skilled Android developer with a passion for building intuitive and high-performance mobile applications","url":"https:\/\/www.vdocipher.com\/blog\/author\/nafis\/"}]}},"yoast":{"focuskw":"flutter live streaming","title":"Developing a Low-Latency, Fully-Featured Flutter Live Streaming App","metadesc":"Learn step-by-step how to create a scalable, interactive, low latency Flutter live streaming app using VdoCipher\u2019s robust infrastructure.","linkdex":"71","metakeywords":"","meta-robots-noindex":"","meta-robots-nofollow":"","meta-robots-adv":"","canonical":"","redirect":"","opengraph-title":"","opengraph-description":"","opengraph-image":"","twitter-title":"","twitter-description":"","twitter-image":""},"_links":{"self":[{"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/posts\/18208","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/users\/30"}],"replies":[{"embeddable":true,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/comments?post=18208"}],"version-history":[{"count":39,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/posts\/18208\/revisions"}],"predecessor-version":[{"id":20885,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/posts\/18208\/revisions\/20885"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/media\/18214"}],"wp:attachment":[{"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/media?parent=18208"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/categories?post=18208"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.vdocipher.com\/blog\/wp-json\/wp\/v2\/tags?post=18208"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}