<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Aman Kumar</title>
    <description>The latest articles on Forem by Aman Kumar (@thisisamank).</description>
    <link>https://forem.com/thisisamank</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1182832%2F25cf4565-4a53-41e0-b294-e0b59a5f86eb.jpeg</url>
      <title>Forem: Aman Kumar</title>
      <link>https://forem.com/thisisamank</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thisisamank"/>
    <language>en</language>
    <item>
      <title>Render Video Tracks From WebRTC Using Flutter PlatformViews</title>
      <dc:creator>Aman Kumar</dc:creator>
      <pubDate>Mon, 16 Oct 2023 11:24:31 +0000</pubDate>
      <link>https://forem.com/dyte/render-video-tracks-from-webrtc-using-flutter-platformviews-4o6j</link>
      <guid>https://forem.com/dyte/render-video-tracks-from-webrtc-using-flutter-platformviews-4o6j</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ntS91Yjy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/288f1y6smnu8dhn84sle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ntS91Yjy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/288f1y6smnu8dhn84sle.png" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flutter, unlike native Android, iOS, or even React Native apps, does not use system drawing primitives for rendering your application. This blog will give you all the theory of how, what, and why of using Flutter PlatformViews and how WebRTC(lib) VideoTracks can be rendered in your Flutter applications using PlatformViews.&lt;/p&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;Before we start our discussion on how &lt;code&gt;PlatformViews&lt;/code&gt; are rendered, let's discuss how Flutter generally draws its UI.&lt;/p&gt;

&lt;p&gt;Flutter paints its UI from scratch every time using its graphics engine, &lt;code&gt;Impeller&lt;/code&gt;. Flutter draws every pixel on the screen, giving developers a high degree of control over the UI.  &lt;/p&gt;

&lt;p&gt;Flutter uses 3 threads to render its UIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI thread&lt;/li&gt;
&lt;li&gt;Platform thread&lt;/li&gt;
&lt;li&gt;Raster thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI thread is where your Dart code runs. The Platform thread is responsible for creating widgets, calculating the layout, and other layout-related tasks. Once this is done, the layout tree is delegated to the Raster thread, which converts the tree into actual pixels on the screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dart code runs on the UI thread&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see how this fits with our use case of making video available from the native side to Flutter.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;WebRTC connection&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;There are multiple ways through which you can render your &lt;code&gt;libwebrtc&lt;/code&gt; video tracks on Flutter, like for Android, you can&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;SurfaceTextureRenderer&lt;/code&gt; to render the VideoTrack, make the Texture available to Flutter using TextureRegistry API, and &lt;a href="https://api.flutter.dev/flutter/widgets/Texture-class.html"&gt;render the Texture in Flutter&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;SurfaceViewRenderer&lt;/code&gt; to render the VideoTrack as an AndroidView, and use PlatformViews to use the native view in Flutter.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this blog are going to talk about how the 2nd approach works under the hood. Specifically, what and why of &lt;code&gt;PlatformViews&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What are Flutter PlatformViews?
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;PlatformViews&lt;/code&gt; are used when we want to use native views as a Flutter widget. There are two ways we can create PlatformView in Flutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;strong&gt;Hybrid composition&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In Hybrid composition, Flutter creates a special type of view and asks Android/iOS to create a corresponding view and embeds the native view into its own widget tree.&lt;/p&gt;

&lt;p&gt;Below is how you can implement &lt;code&gt;PlatformViews&lt;/code&gt; by using the Hybrid composition method -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This is used in the platform side to register the view.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;platform-view-type&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Pass parameters to the platform side.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PlatformViewLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;viewType:&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;surfaceFactory:&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AndroidViewSurface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;controller:&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;AndroidViewController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;gestureRecognizers:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Factory&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OneSequenceGestureRecognizer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;{},&lt;/span&gt;
        &lt;span class="nl"&gt;hitTestBehavior:&lt;/span&gt; &lt;span class="n"&gt;PlatformViewHitTestBehavior&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;opaque&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nl"&gt;onCreatePlatformView:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PlatformViewsService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initSurfaceAndroidView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;viewType:&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;layoutDirection:&lt;/span&gt; &lt;span class="n"&gt;TextDirection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ltr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;creationParams:&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;creationParamsCodec:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;StandardMessageCodec&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nl"&gt;onFocus:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFocusChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOnPlatformViewCreatedListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onPlatformViewCreated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a breakdown of what each part does:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Variables initialization&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;viewType&lt;/code&gt; is a unique identifier for the native view. This should match the identifier used in the native Android code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creationParams&lt;/code&gt; is a map that can hold any parameters you want to pass to the native view for its initialization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. PlatformViewLink widget&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This widget serves as a bridge between the Flutter framework and the native view. It takes in the &lt;code&gt;viewType&lt;/code&gt; and two factory functions: &lt;code&gt;surfaceFactory&lt;/code&gt; and &lt;code&gt;onCreatePlatformView&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. surfaceFactory function&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function returns an &lt;code&gt;AndroidViewSurface&lt;/code&gt; widget, which is responsible for displaying the native Android view.&lt;/li&gt;
&lt;li&gt;It takes a &lt;code&gt;controller&lt;/code&gt; argument, an instance of &lt;code&gt;AndroidViewController&lt;/code&gt;, which is used to control the native Android view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gestureRecognizers&lt;/code&gt; specifies which gestures the native view should consume. In this example, it's set to an empty set, meaning the native view won't consume any gestures.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hitTestBehavior&lt;/code&gt; is set to &lt;code&gt;PlatformViewHitTestBehavior.opaque&lt;/code&gt;, which means the native view will block touches to underlying Flutter widgets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. onCreatePlatformView function&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function initializes the native Android view and returns an instance of it.&lt;/li&gt;
&lt;li&gt;It uses &lt;code&gt;PlatformViewsService.initSurfaceAndroidView&lt;/code&gt; to initialize the native view, passing in various parameters like &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;viewType&lt;/code&gt;, and &lt;code&gt;creationParams&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;onFocus&lt;/code&gt; callback is also defined, which is triggered when the native view gains focus.&lt;/li&gt;
&lt;li&gt;A listener is added to notify when the Android view is created successfully.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By using this &lt;code&gt;build&lt;/code&gt; method in your Flutter app, you can seamlessly integrate a native Android view into your Flutter widget tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;strong&gt;Virtual display mode&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In virtual display mode, Flutter creates an offscreen &lt;code&gt;android.view.Surface&lt;/code&gt;, which is a drawing surface to render graphics. It’s like a blank canvas. Then the native view renders its content onto this surface. Flutter then takes the content of that surface and uses it as a texture within its rendering pipeline. This texture is drawn where the platform view should appear.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;iOS&lt;/code&gt; only supports Hybrid composition.&lt;/p&gt;

&lt;p&gt;Below is how you can implement virtual display mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This is used in the platform side to register the view.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'&amp;lt;platform-view-type&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Pass parameters to the platform side.&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;{};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;AndroidView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;viewType:&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;layoutDirection:&lt;/span&gt; &lt;span class="n"&gt;TextDirection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ltr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;creationParams:&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;creationParamsCodec:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;StandardMessageCodec&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;AndroidView widget&lt;/strong&gt;:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This widget is used to display the native Android view within the Flutter app.&lt;/li&gt;
&lt;li&gt;It takes several parameters:&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viewType&lt;/code&gt; specifies the type of Android view to create.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;layoutDirection&lt;/code&gt; sets the text direction, which is left-to-right (&lt;code&gt;TextDirection.ltr&lt;/code&gt;) in this example.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creationParams&lt;/code&gt; are the initial parameters to pass to the Android view.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;creationParamsCodec&lt;/code&gt; specifies how to encode &lt;code&gt;creationParams&lt;/code&gt;. The &lt;code&gt;StandardMessageCodec&lt;/code&gt; is used for encoding basic types like strings, numbers, and collections.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Native implementation
&lt;/h3&gt;

&lt;p&gt;On the native side, we need to create the view that we need to serve, create a factory, and register it so that the native platform can create views whenever it is asked by Flutter.&lt;/p&gt;

&lt;p&gt;To do this in Android we do -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NativeViewFactory&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PlatformViewFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StandardMessageCodec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;?):&lt;/span&gt; &lt;span class="nc"&gt;PlatformView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;creationParams&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;?&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;NativeView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;viewId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we have to register this factory in the flutter engine -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platformViewRegistry&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerViewFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;platform-view-type&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NativeViewFactory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Impact
&lt;/h3&gt;

&lt;p&gt;Before Android 10, hybrid composition required a lot of to and fro between main memory and GPU to compose native views with flutter widgets, after Android 10, copying is done only once, which makes it very performant.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before Android 10&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dyte.io/blog/content/images/2023/08/Flutter-Platviews-I.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rSqBsEYi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dyte.io/blog/content/images/2023/08/Flutter-Platviews-I.png" alt="Flutter PlatformView I" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After Android 10:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dyte.io/blog/content/images/2023/09/Flutter-Platfoem-Views-II.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--89CslPrL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dyte.io/blog/content/images/2023/09/Flutter-Platfoem-Views-II.png" alt="" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a small table that can help you decide which method to choose:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dyte.io/blog/content/images/2023/08/Screenshot-2023-08-31-at-9.50.13-PM.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3RM_BRtw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dyte.io/blog/content/images/2023/08/Screenshot-2023-08-31-at-9.50.13-PM.png" alt="" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;What did we choose at Dyte?&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;At Dyte, our Flutter SDK is a wrapper over the Android and iOS SDKs, and we don't load any WebRTC video/audio streams directly. We use &lt;a href="https://docs.dyte.io/android-core/local-user/introduction#get-local-user-video-view"&gt;&lt;code&gt;VideoView&lt;/code&gt;&lt;/a&gt; from our Android SDK to display the video, which is then served as a PlatformView. We prefer a hybrid composition due to its performance benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;We have two Flutter SDKs: dyte core and dyte uikit. Dyte core has no knowledge of where the &lt;code&gt;VideoView&lt;/code&gt; (which is a &lt;code&gt;PlatformView&lt;/code&gt;) is going to be used. It might be possible that the &lt;code&gt;VideoView&lt;/code&gt; for a single participant is used on multiple screens. Let’s call this &lt;code&gt;VideoView&lt;/code&gt; as &lt;code&gt;FlutterVideoView&lt;/code&gt; since &lt;code&gt;VideoView&lt;/code&gt; is also present in our &lt;a href="https://docs.dyte.io/android-core"&gt;Android SDK&lt;/a&gt; and is a crucial part of this discussion.&lt;/p&gt;

&lt;p&gt;We wrote a &lt;code&gt;PlatformView&lt;/code&gt;, which serves &lt;code&gt;VideoView&lt;/code&gt;. Let’s call it &lt;code&gt;AndroidVideoView&lt;/code&gt;. Dyte core is dependent on our core mobile SDKs and &lt;code&gt;VideoView&lt;/code&gt; is a &lt;code&gt;View&lt;/code&gt; in Android.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We know that one &lt;code&gt;View&lt;/code&gt; can be part of only one &lt;code&gt;ViewGroup&lt;/code&gt; at any moment of time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dyte.io/blog/content/images/2023/08/Flutter-Platform-III.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--83vnMJ46--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dyte.io/blog/content/images/2023/08/Flutter-Platform-III.png" alt="Flutter PlatformView III" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let’s keep this in the back of the mind that Flutter has no direct support to detect the visibility of a widget.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The problem we were facing was when we used &lt;code&gt;FlutterVideoView&lt;/code&gt; for the same participant on two different screens. For the sake of simplicity, let’s call it screen A and screen B. Now when the user navigates from screen A to screen B everything was working smoothly as screen B was creating a fresh &lt;code&gt;FlutterVideoView&lt;/code&gt;, which internally created a new instance of &lt;code&gt;AndroidVideoView&lt;/code&gt;, so the &lt;code&gt;VideoView&lt;/code&gt; is now attached to a new &lt;code&gt;ViewGroup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem was when the user navigated back from screen B to screen A, there was no way to detect if &lt;code&gt;FlutterVideoView&lt;/code&gt; widget had gone from background to foreground so we could call &lt;code&gt;render()&lt;/code&gt; on the &lt;code&gt;AndroidVideoView&lt;/code&gt;, which is a PlatformView in our Flutter SDK. &lt;code&gt;render()&lt;/code&gt; method calls the &lt;code&gt;render()&lt;/code&gt; of &lt;code&gt;VideoView&lt;/code&gt;, which internally takes care of removing the view from the old &lt;code&gt;ViewGroup&lt;/code&gt; and renders the video track.&lt;/p&gt;

&lt;p&gt;On the Dyte UI kit, we can get the lifecycle methods if the screen has been changed, but that would not solve the actual problem of the view not getting refreshed on its own. To solve this, we used a community plugin, &lt;code&gt;[visibility_detector](https://pub.dev/packages/visibility_detector)&lt;/code&gt; in Dyte core flutter SDK. It gave us a widget that had callbacks that get triggered when its child widget’s visibility is changed.&lt;/p&gt;

&lt;p&gt;Now the second part of the problem was to find out the &lt;code&gt;PlatformView&lt;/code&gt;, which was associated with the widget and call &lt;code&gt;render()&lt;/code&gt; on it. To tackle this we had to retrieve the &lt;code&gt;View&lt;/code&gt; from the Flutter engine since all the &lt;code&gt;PlatformView&lt;/code&gt;s are created by the Flutter engine itself.&lt;/p&gt;

&lt;p&gt;To tackle this, we cached the &lt;code&gt;FlutterEngine&lt;/code&gt; using &lt;a href="https://api.flutter.dev/javadoc/io/flutter/embedding/engine/FlutterEngineCache.html"&gt;&lt;code&gt;FlutterEngineCache&lt;/code&gt;&lt;/a&gt;. This allowed us to access the &lt;code&gt;PlatformView&lt;/code&gt; by its &lt;code&gt;viewId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;FlutterEngineCache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DyteFlutterEngine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flutterPluginBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flutterEngine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Through the engine, we can access the &lt;code&gt;PlatformView&lt;/code&gt; with the help of &lt;code&gt;viewId&lt;/code&gt;, which is assigned to every &lt;code&gt;PlatformView&lt;/code&gt; when it is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="nl"&gt;onCreatePlatformView:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PlatformViewsService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initSurfaceAndroidView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;viewType:&lt;/span&gt; &lt;span class="n"&gt;viewType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;layoutDirection:&lt;/span&gt; &lt;span class="n"&gt;TextDirection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ltr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;creationParams:&lt;/span&gt; &lt;span class="n"&gt;creationParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nl"&gt;reationParamsCodec:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;StandardMessageCodec&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
         &lt;span class="nl"&gt;onFocus:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFocusChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="c1"&gt;// Here we assign the view id.&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addOnPlatformViewCreatedListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setNativeViewId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access the native view, we did&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;targetView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FlutterEngineCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s"&gt;"DyteFlutterEngine"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platformViewsController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPlatformViewById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;AndroidVideoView&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we call the &lt;code&gt;render()&lt;/code&gt; method on this view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;targetView&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After all these steps, we clear the cached &lt;code&gt;FlutterEngine&lt;/code&gt; to free up the memory.  This helped us solve a critical issue in our Flutter video SDK.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;This concludes our take on Flutter PlatformViews and how we use it at &lt;code&gt;Dyte&lt;/code&gt;. Video is the most essential part of our &lt;a href="https://docs.dyte.io/flutter"&gt;Flutter SDK&lt;/a&gt;, and the Flutter ecosystem has provided us with great utilities to deal with it.&lt;/p&gt;

&lt;p&gt;On top of it, we have made it seamless for you to have feature-rich &lt;a href="https://dyte.io/video-sdk"&gt;audio video conferencing&lt;/a&gt; in your app with the least hassle. Stay tuned for more engineering blogs, we talk about behind the scenes, WebRTC, and cool things that can be built upon Dyte. Check out our blog &lt;a href="https://dyte.io/blog"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
