<?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>DEV Community: Dominik Roszkowski</title>
    <description>The latest articles on DEV Community by Dominik Roszkowski (@orestesgaolin).</description>
    <link>https://dev.to/orestesgaolin</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%2F166837%2F70a27a6a-49d3-4183-a12f-6ba78ca29bb1.jpeg</url>
      <title>DEV Community: Dominik Roszkowski</title>
      <link>https://dev.to/orestesgaolin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/orestesgaolin"/>
    <language>en</language>
    <item>
      <title>Flutter: Using Android Intent via native interop with jnigen</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Fri, 19 Sep 2025 19:20:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/using-android-intent-via-native-interop-with-jnigen-192</link>
      <guid>https://dev.to/orestesgaolin/using-android-intent-via-native-interop-with-jnigen-192</guid>
      <description>&lt;p&gt;In this post I’ll show how to access some of the Android APIs directly in Dart/Flutter. The final result will be a plugin allowing to send some basic Intents to the Android system, like opening the e-mail client or selecting a contact.&lt;/p&gt;

&lt;center&gt;

  
  

&lt;/center&gt;

&lt;p&gt;We wanted to open user’s default e-mail client to send a properly formatted message, but the typical &lt;code&gt;package:url_launcher&lt;/code&gt; approach of creating a &lt;code&gt;mailto:&lt;/code&gt; was too flaky. Android &lt;a href="https://developer.android.com/guide/components/intents-common.html#Email" rel="noopener noreferrer"&gt;allows to provide a specific intent&lt;/a&gt; of type &lt;code&gt;ACTION_SENDTO&lt;/code&gt; that will query the system for the best app to handle it. I created bindings for this Intent call with jnigen and here’s a bit of overview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Create new Flutter plugin and modify the &lt;code&gt;pubspec.yaml&lt;/code&gt; and &lt;code&gt;jnigen.yaml&lt;/code&gt; to match the example in the &lt;a href="https://github.com/dart-lang/native/tree/main/pkgs/jnigen/example" rel="noopener noreferrer"&gt;jnigen repository&lt;/a&gt;. This will only work on Android, hence:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter create &lt;span class="nt"&gt;--template&lt;/span&gt; plugin &lt;span class="nt"&gt;--platforms&lt;/span&gt; android android_intent
&lt;span class="nb"&gt;cd &lt;/span&gt;android_intent/example
flutter build apk &lt;span class="nt"&gt;--release&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After cleaning up the code and dependencies &lt;strong&gt;make sure to build your Android example app in release&lt;/strong&gt;. You can also open it in Android Studio to check if everything is correct.&lt;/p&gt;

&lt;p&gt;This setup assumes following package versions and works as of July 2025:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jnigen&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.14.0&lt;/span&gt;
&lt;span class="na"&gt;jni&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^0.14.2&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  jnigen setup
&lt;/h2&gt;

&lt;p&gt;The basic setup of jnigen plugin works fine for me although it results with over 20k generated lines of Dart code. To be able to access Android APIs you need to specify all the classes (and their dependencies e.g. &lt;code&gt;android.net.Uri&lt;/code&gt;) that would normally be imported in Java/Kotlin code. The following &lt;code&gt;jnigen.yaml&lt;/code&gt; file worked for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;android_sdk_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add_gradle_deps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;android_example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example/"&lt;/span&gt;

&lt;span class="na"&gt;source_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android/src/main/java"&lt;/span&gt;
&lt;span class="na"&gt;classes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.content.Intent"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.content.Context"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.app.Activity"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.net.Uri"&lt;/span&gt;

&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lib/src/bindings.dart"&lt;/span&gt;
    &lt;span class="na"&gt;structure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;single_file"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you run the jnigen code generator, you should be able to use the generated bindings directly in Dart code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub run jnigen &lt;span class="nt"&gt;--config&lt;/span&gt; jnigen.yaml

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage in Dart
&lt;/h2&gt;

&lt;p&gt;To send a simple Intent to open the e-mail client, you can use the following Dart code:&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACTION_SENDTO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mailto:"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;

    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putExtra&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EXTRA_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;JArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"example@example.com"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putExtra&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EXTRA_SUBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Subject"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putExtra&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EXTRA_TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Body text"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;putExtra&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EXTRA_CC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;JArray&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cc@example.com"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;()]),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FLAG_ACTIVITY_NEW_TASK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;contextInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Jni&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCachedApplicationContext&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;contextInstance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Error opening email: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&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;What is quite cool it follows 1:1 the code from the &lt;a href="https://developer.android.com/guide/components/intents-common#Email" rel="noopener noreferrer"&gt;Android documentation&lt;/a&gt;:&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;composeEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&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;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&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;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ACTION_SENDTO&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mailto:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Only email apps handle this.&lt;/span&gt;
        &lt;span class="nf"&gt;putExtra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EXTRA_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;putExtra&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;EXTRA_SUBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packageManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&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;Obviously there are several slightly annoying differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have to cast Dart types to Jni types, e.g. &lt;code&gt;toJString()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You have to use &lt;code&gt;JArray.of&amp;lt;JString&amp;gt;()&lt;/code&gt; to create an array of strings&lt;/li&gt;
&lt;li&gt;You have to use &lt;code&gt;putExtra$21&lt;/code&gt; and &lt;code&gt;putExtra$8&lt;/code&gt; methods as Dart does not support method overloading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the flipside, the &lt;a href="https://pub.dev/packages/jni" rel="noopener noreferrer"&gt;package:jni&lt;/a&gt; comes with few convenience methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Jni.getCachedApplicationContext()&lt;/code&gt; to get the application context which I can cast to &lt;code&gt;Context&lt;/code&gt; and use it to start the intent.&lt;/li&gt;
&lt;li&gt;not shown here &lt;code&gt;Jni.getCurrentActivity()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sending Intents with results
&lt;/h2&gt;

&lt;p&gt;It isn’t directly possible to receive Intent results via Activity.onActivityResult(), so I had to come up with a workaround. The main inspiration was &lt;a href="https://pub.dev/packages/receive_intent" rel="noopener noreferrer"&gt;package:receive_intent&lt;/a&gt;, and my previous attempt of using &lt;a href="https://github.com/orestesgaolin/native_interop_presentation/blob/main/appupdate/android/src/main/kotlin/dev/roszkowski/appupdate/InstallStateUpdatedListenerProxy.kt" rel="noopener noreferrer"&gt;proxy with a listener interface&lt;/a&gt;. There may be other ways in the future to achieve this – see &lt;a href="https://github.com/dart-lang/native/issues/2435" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; for details.&lt;/p&gt;

&lt;p&gt;Anyway, to receive intent results like this, you need to override the &lt;code&gt;onActivityResult&lt;/code&gt; of your main Activity.&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="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;REQUEST_SELECT_CONTACT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;selectContact&lt;/span&gt;&lt;span class="p"&gt;()&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;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ACTION_PICK&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ContactsContract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contacts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CONTENT_TYPE&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolveActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packageManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;startActivityForResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;REQUEST_SELECT_CONTACT&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="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onActivityResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&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;resultCode&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;REQUEST_SELECT_CONTACT&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;RESULT_OK&lt;/span&gt;&lt;span class="p"&gt;)&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;contactUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Uri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;
        &lt;span class="c1"&gt;// Do something with the selected contact at contactUri.&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;&lt;code&gt;FlutterPlugin&lt;/code&gt; comes with a &lt;code&gt;ActivityAware&lt;/code&gt; interface that allows you to register a result listener via &lt;code&gt;ActivityPluginBinding&lt;/code&gt;:&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="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onAttachedToActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ActivityPluginBinding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WeakReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addActivityResultListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ARLP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"addActivityResultListener called with intent: $intent"&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;So to make that work I had to implement a typical Flutter plugin class that would keep &lt;code&gt;OnResultListener&lt;/code&gt; interface. This interface will later be implemented in Dart.&lt;/p&gt;

&lt;p&gt;This also means that now my plugin includes a Kotlin class of its own so it needs to be registered in the &lt;code&gt;pubspec.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;flutter:
&lt;/span&gt;  plugin:
    platforms:
      android:
        ffiPlugin: true
&lt;span class="gi"&gt;+ package: com.example.android_intent
+ pluginClass: ActivityResultListenerProxy
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up I created a Kotlin class &lt;code&gt;ActivityResultListenerProxy&lt;/code&gt;:&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="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActivityResultListenerProxy&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FlutterPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ActivityAware&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WeakReference&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OnResultListener&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OnResultListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&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;resultCode&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;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Keep&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&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;resultCode&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;intent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;onResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resultCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dataString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&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;onAttachedToActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ActivityPluginBinding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WeakReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addActivityResultListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ARLP"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"onAttachedToActivity called with intent: $intent"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;onResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;intent&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="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onDetachedFromActivityForConfigChanges&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// other methods&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One challange was to get hold of the plugin instance that would be created by Flutter. To make it work I made it a singleton and added a static companion object that would hold the instance. Plugin cannot be Kotlin’s &lt;code&gt;object&lt;/code&gt;.&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="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActivityResultListenerProxy&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FlutterPlugin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ActivityAware&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OnResultListener&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@Volatile&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ActivityResultListenerProxy&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;

        &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ActivityResultListenerProxy&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;instance&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;synchronized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nc"&gt;ActivityResultListenerProxy&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&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="nd"&gt;@Keep&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;setOnResultListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OnResultListener&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// other methods&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then after updating my jnigen.yaml I was able to start intent with result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;android_sdk_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add_gradle_deps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;android_example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example/"&lt;/span&gt;

&lt;span class="na"&gt;source_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android/src/main/java"&lt;/span&gt;
&lt;span class="na"&gt;classes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.content.Intent"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.content.Context"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.app.Activity"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.net.Uri"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;android.provider.ContactsContract"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;com.example.android_intent.ActivityResultListenerProxy"&lt;/span&gt;

&lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lib/src/bindings.dart"&lt;/span&gt;
    &lt;span class="na"&gt;structure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;single_file"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time I had to use &lt;code&gt;Activity&lt;/code&gt; to start the intent and register the result listener.&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;selectContact&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ACTION_PICK&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ContactsContract$Contacts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CONTENT_TYPE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ActivityResultListenerProxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Companion&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="n"&gt;listener&lt;/span&gt;&lt;span class="o"&gt;!.&lt;/span&gt;&lt;span class="na"&gt;setOnResultListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;ActivityResultListenerProxy$OnResultListener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;implement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;$ActivityResultListenerProxy$OnResultListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;onResult:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'ARLP Selected contact: &lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s"&gt;, request: &lt;/span&gt;&lt;span class="si"&gt;$request&lt;/span&gt;&lt;span class="s"&gt;, data: &lt;/span&gt;&lt;span class="si"&gt;$data&lt;/span&gt;&lt;span class="s"&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="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Activity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Jni&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentActivity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startActivityForResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Error selecting contact: &lt;/span&gt;&lt;span class="si"&gt;$e&lt;/span&gt;&lt;span class="s"&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;I experimented a bit more with other Intents like selecting an image from camera and they all seemed feasible. It would take a bit more API design to make it work, but nonethelss it’s quite cool to use Android APIs directly in Dart code.&lt;/p&gt;

&lt;p&gt;You can find the full source code in my &lt;a href="https://github.com/orestesgaolin/native_interop_presentation/tree/main/android_intent" rel="noopener noreferrer"&gt;native_interop repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>kotlin</category>
      <category>interop</category>
      <category>android</category>
    </item>
    <item>
      <title>Native interop with Kotlin/Java in Flutter</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Mon, 20 Jan 2025 10:00:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/native-interop-with-kotlinjava-in-flutter-3oag</link>
      <guid>https://dev.to/orestesgaolin/native-interop-with-kotlinjava-in-flutter-3oag</guid>
      <description>&lt;p&gt;Last year during the Fluttercon session about future of native interop I learned about this new tool called &lt;a href="https://pub.dev/packages/jnigen" rel="noopener noreferrer"&gt;jnigen&lt;/a&gt;. It's became quite stable recently and I wanted to try it out again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some background
&lt;/h2&gt;

&lt;p&gt;In the past I used Xamarin and back then it was quite natural to invoke platform APIs from C# through something called bindings. In other words classes like &lt;code&gt;Activity&lt;/code&gt;, &lt;code&gt;Context&lt;/code&gt;, &lt;code&gt;NSUrl&lt;/code&gt;, &lt;code&gt;UIView&lt;/code&gt; were accessible directly from C# without any additional glue code. It was also possible with a bit of work to expose &lt;a href="https://github.com/aloisdeniel/Xamarin.Bindings" rel="noopener noreferrer"&gt;most of the native libraries in a similar fashion&lt;/a&gt;. Sort of similar thing is also available in &lt;a href="https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-connect-to-apis.html" rel="noopener noreferrer"&gt;Kotlin Multiplatform&lt;/a&gt; where you can access import namespaces like &lt;code&gt;platform.Foundation.NSUUID&lt;/code&gt;. They even do some experiments with using &lt;a href="https://touchlab.co/composeswiftbridge" rel="noopener noreferrer"&gt;SwiftUI views in Compose&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current state of native interop in Dart/Flutter
&lt;/h2&gt;

&lt;p&gt;Some might say that number of ways to interact with the platform from Dart is getting out of hand, sometimes leading to people choosing not to use native APIs at all or switch between random wrappers from pub.dev.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;When interacting with Java/Kotlin/Swift classes in Flutter you need to go via plugin that either includes some glue code using platform channels, or can use some type-safe approach like pigeon or protobuf. Most of the plugins are wrappers around platform-specific APIs e.g. to show notifications or access shared preferences. For some more advanced scenarios you may want to look into &lt;a href="https://docs.flutter.dev/platform-integration/android/c-interop" rel="noopener noreferrer"&gt;dart:ffi&lt;/a&gt; but the learning curve is quite steep for a typical mobile developer.&lt;/p&gt;

&lt;p&gt;Even with the simplest method channel you have to write very similar code 3 times: in the platform interface, in the implementation for each platform, and then handling of it on the native side. In my case I just write it manually (+LLM) as it's typically faster than any of the code generation methods I tried before.&lt;/p&gt;

&lt;p&gt;From what we know Flutter team is actively working on enabling &lt;em&gt;automatic&lt;/em&gt; direct native interop, see the short video below for some recent updates from "Flutter in Production" event (Dec 2024):&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PRo98xNvyjQ"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  jnigen
&lt;/h2&gt;

&lt;p&gt;The promise of &lt;a href="https://pub.dev/packages/jnigen" rel="noopener noreferrer"&gt;jnigen&lt;/a&gt; is to generate Dart bindings from arbitrary Kotlin/Java code. How does it work:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;jnigen scans compiled JAR files or Java source code to generate a description of the API, then uses it to generate Dart bindings. The Dart bindings call the C bindings, which in-turn call the Java functions through JNI. Shared functionality and base classes are provided through the support library, package:jni.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a bit roundabout way, but it feels more understandable than what I remember from Xamarin bindings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple example
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is not meant to be a tutorial how to use jnigen, but rather an inspiration piece :)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A quite simple example can be found in the project repo where they call a suspend Kotlin function from Dart.&lt;/p&gt;

&lt;p&gt;Given a simple Kotlin class:&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="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.annotation.Keep&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlinx.coroutines.*&lt;/span&gt;

&lt;span class="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;thinkBeforeAnswering&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"42"&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;after some code generation you can simply instantiate it and invoke the function with in async-await fashion:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;thinkBeforeAnswering&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// prints 42 after a while;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, what is quite cool, you can also invoke functions synchronously. Given this Java class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.in_app_java&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.app.Activity&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.widget.Toast&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.annotation.Keep&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AndroidUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;showToast&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt; &lt;span class="n"&gt;mainActivity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CharSequence&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mainActivity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runOnUiThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Toast&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;makeText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mainActivity&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simplified invocation would look like this:&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="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:jni/jni.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Jni&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentActivity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;AndroidUtils&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might have noticed you get some handy helpers to retrieve the current activity or context.&lt;/p&gt;

&lt;p&gt;If you ask me, this is exactly what I was looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example with platform view
&lt;/h2&gt;

&lt;p&gt;To play a bit more I wanted to rewrite some of actual production code from the app I'm working on to use jnigen instead of method channels. I've been using &lt;a href="https://pub.dev/packages/flutter_pdfview" rel="noopener noreferrer"&gt;a fork of this pdf plugin&lt;/a&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt; that not only is a classic Flutter plugin, but also includes a platform view.&lt;/p&gt;

&lt;p&gt;After getting rid of the platform channel calls I had to figure out how to control the &lt;code&gt;pdfView&lt;/code&gt; instance that is hosted in the platform view. The simplest way I found was to keep it &lt;a href="https://github.com/orestesgaolin/native_interop_presentation/blob/10b51ddf83a00d612a9d6cf276a2a1e61b978f13/pdf_viewer/packages/pdf/android/src/main/java/com/example/pdf/FlutterPDFView.java#L23" rel="noopener noreferrer"&gt;in a static field&lt;/a&gt; that gets accessed through &lt;a href="https://github.com/orestesgaolin/native_interop_presentation/blob/main/pdf_viewer/packages/pdf/android/src/main/java/com/example/pdf/PDFViewController.java" rel="noopener noreferrer"&gt;a god-like controller class&lt;/a&gt;. &lt;code&gt;FlutterPDFView&lt;/code&gt; is slightly modified platform view implementation from the original plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PDFViewController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setPage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FlutterPDFView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pdfView&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;FlutterPDFView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pdfView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jumpTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Having my &lt;code&gt;PDFViewController&lt;/code&gt; that can access the plugin's platform view instance I generated the bindings with &lt;code&gt;jnigen&lt;/code&gt; and was able to instantiate this class in Dart. What is super exciting is that when referencing the instance in Dart you get to see the exact same memory address as on the native side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2txvvbl7bpu7p62zupq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2txvvbl7bpu7p62zupq.png" alt="Debugging the PDFViewController class in Dart" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms4lrs5q8ntntc53rcsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms4lrs5q8ntntc53rcsg.png" alt="Debugging the PDFViewController class in Java" width="699" height="570"&gt;&lt;/a&gt;&lt;/p&gt;
Note the same memory address when debugging PDFViewController class from Dart and Java



&lt;p&gt;I needed a way to get notified about page changes from the native side. There's no support for generating bindings for streams or similar communication channels, so you have to pass a listener that can include a callback to get invoked.&lt;/p&gt;

&lt;p&gt;After some back and forth I learned that you can generate bindings for Java interfaces that later get implemented in Dart. It blew my mind. In other words I get to create a Dart class that in runtime is implementing a Java interface. This lets me pass it to my previously instantiated class and use it on the Java side.&lt;/p&gt;

&lt;p&gt;Given &lt;a href="https://github.com/orestesgaolin/native_interop_presentation/blob/main/pdf_viewer/packages/pdf/android/src/main/java/com/example/pdf/PDFStatusListener.java" rel="noopener noreferrer"&gt;this Java interface&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Keep&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PDFStatusListener&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onLoaded&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onPageChanged&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onLinkRequested&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onDisposed&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can instantiate the implementation of it in Dart:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PDFStatusListener&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;implement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$PDFStatusListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;onLoaded$async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;onPageChanged$async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;onError$async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;onLinkRequested$async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;onDisposed$async&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;onLoaded:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'PDF Loaded'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nl"&gt;onPageChanged:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'PDF page: &lt;/span&gt;&lt;span class="si"&gt;$page&lt;/span&gt;&lt;span class="s"&gt;, total: &lt;/span&gt;&lt;span class="si"&gt;$total&lt;/span&gt;&lt;span class="s"&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;onError:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'PDF error: &lt;/span&gt;&lt;span class="si"&gt;${string?.toDartString()}&lt;/span&gt;&lt;span class="s"&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;onLinkRequested:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JString&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Link: &lt;/span&gt;&lt;span class="si"&gt;${string?.toDartString()}&lt;/span&gt;&lt;span class="s"&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;onDisposed:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'PDF disposed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;release&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="n"&gt;example&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPdfStatusListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being able to pass an instance of class as listener allows me to convert it to any form I like whether it's a Dart stream or just a void callback. It still requires writing some wrappers and glue code, but somehow it feels more valuable than repetitive platform channels.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Swift
&lt;/h2&gt;

&lt;p&gt;There's experimental bindings generator for Swift: &lt;a href="https://github.com/dart-lang/native/tree/swiftgen2/pkgs/swiftgen" rel="noopener noreferrer"&gt;swiftgen&lt;/a&gt;. I tried it only once, but will definitely try to reproduce the same example and perhaps share my findings here.&lt;/p&gt;

&lt;p&gt;Hope you enjoyed this and perhaps you'll try it for yourself. Truth is, without Flutter developers getting interested, there's no real incentive to push forward.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;




&lt;p&gt;Other resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow Hossein for most up-to-date news about native interop: &lt;a href="https://x.com/YousefiDash" rel="noopener noreferrer"&gt;https://x.com/YousefiDash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/jnigen" rel="noopener noreferrer"&gt;jnigen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/dart-lang/native/tree/main/pkgs/swiftgen" rel="noopener noreferrer"&gt;swiftgen&lt;/a&gt; - may require branch change&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.droidcon.com/2024/09/02/the-past-present-and-future-of-native-interop/" rel="noopener noreferrer"&gt;The past, present, and future of native interop&lt;/a&gt; - Dart team talk about native interop e.g. JNIgen&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.droidcon.com/2024/10/17/future-of-dart-panel/" rel="noopener noreferrer"&gt;Future of Dart panel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dart-lang/native/milestone/13" rel="noopener noreferrer"&gt;SwiftGen GH project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I've heard several time how people were switching between various audio player packages just because of some missing features or little bugs, notification plugins, camera implementations etc. I wish we wouldn't have to go through this "pick and choose" flow with fundamental capabilities like media playback or basic OS functionalities. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;I keep a private fork that uses different implementation on iOS and builds well with my app. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>What really grinds my gears in Flutter</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Sun, 31 Jul 2022 12:00:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/what-really-grinds-my-gears-in-flutter-1da8</link>
      <guid>https://dev.to/orestesgaolin/what-really-grinds-my-gears-in-flutter-1da8</guid>
      <description>&lt;p&gt;My &lt;a href="https://roszkowski.dev/2022/things-i-love-about-flutter/"&gt;latest post&lt;/a&gt; listing reasons why I enjoy Flutter so much was a bit of a puff piece, frankly. Flutter is definitely one of the most fulfilling technologies out there, but it’s not without its flaws. Let’s discuss some of the things that irritate me the most. We’ll start with the web issues.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is my personal opinion, and even though I have worked with Flutter for quite some time, I may be wrong in some cases. Please let me know if something is incorrect or gets fixed in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BfWDLEM4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2022-07/what-really-grinds-my-gears.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BfWDLEM4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2022-07/what-really-grinds-my-gears.jpg" alt="" width="880" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Web issues
&lt;/h1&gt;

&lt;p&gt;Some of the experienced Flutter developers I personally know would not select Flutter as their primary tool for building a website. It’s a bit of a different discussion when it comes to a very specific set of apps that could as well be a desktop tool like Supernova, Rive, Superlist, or Rows. However, building big scale consumer-facing… &lt;em&gt;portals&lt;/em&gt; entirely in Flutter may be out of question.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong. I use Flutter web regularly e.g. for my presentation companion apps. In my consulting work I participated in several Flutter web projects. It worked mostly fine, but the class of problems that need to be solved is vastly different from what a &lt;em&gt;typical&lt;/em&gt; web developer faces on a daily basis.&lt;/p&gt;

&lt;p&gt;I suspect there’s still a long way before Flutter will feel and work equally well as &lt;em&gt;classic&lt;/em&gt; web apps. Here are some of the most pressing issues I see right now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zNqq65-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/dash.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zNqq65-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/dash.png" alt="" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Announcing my newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make it easier to never miss a new blog post, I decided to start a simple newsletter. It's called &lt;em&gt;Occasional Flutter&lt;/em&gt; and &lt;a href="https://occasionalflutter.substack.com"&gt;I invite you to subscribe today&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Default behavior
&lt;/h2&gt;

&lt;p&gt;Anytime you run a Flutter &lt;strong&gt;mobile&lt;/strong&gt; app, you can quickly deduce this from the little things like &lt;a href="https://github.com/flutter/flutter/issues/11884"&gt;scrolling with two fingers make it go twice as fast&lt;/a&gt; or &lt;a href="https://github.com/flutter/flutter/issues/16322"&gt;rotation of the app looks a little bit off&lt;/a&gt; compared to a native one.&lt;/p&gt;

&lt;p&gt;Similarly, there are things that are odd in Flutter &lt;strong&gt;web&lt;/strong&gt; apps. I immediately notice that keyboard shortcuts or focus behavior are just a little bit different from what I would expect. It can be &lt;em&gt;tweaked&lt;/em&gt; to work similarly as in a simple HTML document with &lt;a href="https://api.flutter.dev/flutter/widgets/Actions-class.html"&gt;Actions&lt;/a&gt; and &lt;a href="https://api.flutter.dev/flutter/widgets/Intent-class.html"&gt;Intents&lt;/a&gt;, but developers rarely remember to handle focus changes, space or tab behavior, cursor decoration etc. [As an exercise open &lt;a href="https://gallery.flutter.dev/#/"&gt;any Flutter web app&lt;/a&gt; and press space, then open &lt;a href="https://twitter.com/OrestesGaolin"&gt;Twitter&lt;/a&gt; and do it again]&lt;/p&gt;

&lt;p&gt;Some other issues worth noting are lack of proper text selection (&lt;a href="http://flutter.dev/go/global-selection"&gt;which hopefully will be fixed with this change&lt;/a&gt;), &lt;a href="https://github.com/flutter/flutter/issues/65504"&gt;not being able to find words with the browser &lt;em&gt;find&lt;/em&gt; box&lt;/a&gt;, and more subtle challenges like bundle size or mobile performance.&lt;/p&gt;

&lt;p&gt;I feel like I need to emphasize that some of the issues mentioned here are not framework bugs, but rather consequences of mobile developers building web apps. As such we tend to make mistakes not understanding how the web form factor differs from a single window mobile universum.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tough choice
&lt;/h2&gt;

&lt;p&gt;When deploying a Flutter web app you’re faced with a choice. You can either run the app with &lt;a href="https://docs.flutter.dev/development/platform-integration/web/renderers"&gt;the HTML renderer or CanvasKit&lt;/a&gt;. By default on mobile the former option is used, whereas desktop defaults to CanvasKit. For basic usage it’s fine, but in practice the mobile experience will usually be worse. There’s no easy way to measure this without &lt;a href="https://github.com/flutter/flutter/issues/52258"&gt;performance overlay&lt;/a&gt;. I noticed some of the best-known Flutter web apps just avoid running on mobile at all.&lt;/p&gt;

&lt;p&gt;Several problems from the previous section are much more perceivable on smaller form factors, which for a client may be extremely unfortunate. Some say&lt;sup&gt;[&lt;a href="https://www.broadbandsearch.net/blog/mobile-desktop-internet-usage-statistics"&gt;1&lt;/a&gt;], [&lt;a href="https://www.perficient.com/insights/research-hub/mobile-vs-desktop-usage"&gt;2&lt;/a&gt;]&lt;/sup&gt;, that nowadays users spend most of their online time (and thus money) via smartphones. There’s a new generation of people growing up now that mobile is their primary tool to access most of the online services. It sounds obvious but delivering subpar quality and performance is out of the question for most of the startups and &lt;em&gt;disruptors&lt;/em&gt; on the market.&lt;/p&gt;

&lt;p&gt;Every quarter the Flutter team organizes a survey to collect feedback like this one. They &lt;a href="https://medium.com/flutter/does-flutter-boost-developer-productivity-475f713724b3"&gt;publish results on medium&lt;/a&gt;. Make sure to take part in the next one, whenever you see a prompt in your IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrolling and physics
&lt;/h2&gt;

&lt;p&gt;Over the last couple of months this part has gotten significantly better, but it’s still interesting to see how different the scrolling behavior is in Flutter apps compared to ordinary webpages or desktop apps. I wish I would understand the technical details more, but in essence, Flutter is emulating scrolling from scratch. This means it has to handle different scroll physics implementations per platform, various input devices, and things like scrollbar on its own. &lt;a href="https://pub.dev/packages/flutter_improved_scrolling"&gt;There were attempts&lt;/a&gt; by the community to make it &lt;em&gt;nicer&lt;/em&gt;, as well as &lt;a href="https://github.com/flutter/flutter/issues/75850"&gt;several&lt;/a&gt; &lt;a href="https://github.com/flutter/flutter/issues/107752"&gt;issues&lt;/a&gt; reporting spotted discrepancies.&lt;/p&gt;

&lt;p&gt;In this animation made in &lt;a href="https://rive.app/"&gt;Rive&lt;/a&gt; I tried to visualize my interpretation of how the scrolling differs in Flutter compared to typical websites. As the scrolling is emulated, it’s Flutter’s responsibility to render elements outside of the viewport once they enter it. Therefore, the browser is not able to leverage existing techniques for preloading images or rendering text (forgive me this slight anthropomorphization of browser and Flutter 😉),&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rive.app/s/UzrvvFRvtUu8JT_9g4GYCg/"&gt;See animation here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Elements like this one add up to make the general feeling of the Flutter web app slightly different than what we’re used to. It’s awesome to have such freedom when building a mobile app toolkit, but it can add a lot of work when deploying a simple webpage.&lt;/p&gt;

&lt;h2&gt;
  
  
  General feeling
&lt;/h2&gt;

&lt;p&gt;As an example for this chapter, head over to a pretty well-executed website of &lt;a href="https://superdeclarative.com/"&gt;Matt Carroll&lt;/a&gt;. It looks all right, it’s relatively heavy on the graphics, and - what’s important - it’s a typical landing page that’s a few times longer than its width. Let’s list some quirks (I hope you don’t mind Matt). On mobile, you can scroll it twice as fast with two fingers. If you pause JS execution it won’t scroll anymore. The scrollbars are not provided by the browser but painted with Flutter, so they appear only when you move your mouse near the edge. You cannot select any text. You cannot enter funny faces 🤪 to send them to Matt with a macOS emoji picker. And if you leave the website open for a couple of minutes, something else happens…&lt;/p&gt;

&lt;h2&gt;
  
  
  GPU load
&lt;/h2&gt;

&lt;p&gt;When building the Flutter web app with CanvasKit it will get rendered mostly by the GPU &lt;sup&gt;[citation needed]&lt;/sup&gt;. In some cases (like the animations-heavy landing page) leaving the app open for 5-10 minutes will make your GPU hot enough that fans need to kick in. It was especially painful when using i9 MacBook Pro with its thermal limitations, but even on new M1 laptops it happens when I leave a Flutter web open. I need to point out that it may be very specific to my workflow with dozens of Chrome tabs open and several memory-heavy apps like Slack or VS Code running in the foreground. Moreover, it can be optimized and some high-quality apps like &lt;a href="https://www.supernova.io/"&gt;Supernova&lt;/a&gt; nail it, but I decided to mention it anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reinventing the wheel
&lt;/h2&gt;

&lt;p&gt;People often say “don’t reinvent the wheel”. However, if not for reinventing the wheel, &lt;a href="https://en.wikipedia.org/wiki/Lunar_Roving_Vehicle#Wheels_and_power"&gt;we wouldn’t be able to ride on the Moon&lt;/a&gt;. Some of the things that &lt;a href="https://docs.flutter.dev/resources/inside-flutte"&gt;the Flutter team had to reinvent&lt;/a&gt; allow for unprecedented flexibility and performance, but at the same time make it difficult to leverage existing APIs (e.g. &lt;a href="https://github.com/flutter/flutter/issues/104082"&gt;undo&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I imagine it’s a difficult spot to be in for the Flutter team. The decision to make a leap into the web ecosystem meant that they have to solve a completely different set of problems than in Android and iOS realms alone. For me, the distance between mobile and web seems larger than for instance between mobile and desktop. This is why my greatest excitement in 2019 was &lt;a href="https://docs.flutter.dev/whats-new#dec-11-2019-flutter-interact-edition-112-release"&gt;the announcement of alpha support for macOS&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Desktop issues
&lt;/h1&gt;

&lt;p&gt;When discussing desktop issues, we need to face a similar truth as in the case of the web. Flutter in its foundations &lt;a href="https://www.youtube.com/watch?v=PnIWl33YMwA"&gt;was meant to be run on small handheld devices 🌌&lt;/a&gt;. Expanding it to cover big screens where multitasking and window management is crucial means there’s a lot to build from scratch. Just &lt;a href="https://github.com/flutter/flutter/pull/108166"&gt;take a look&lt;/a&gt; what kind of details need to be taken into account to correctly handle the scrollbar on macOS.&lt;/p&gt;

&lt;p&gt;The rise of Electron’s popularity and the examples of exceptional desktop apps built with JS/TS show that there’s still huge potential for Flutter despite its past. There are &lt;a href="https://rows.com/"&gt;several&lt;/a&gt; &lt;a href="https://www.superlist.com/"&gt;examples&lt;/a&gt; of apps that deliver amazing quality and experience even though the desktop ecosystem in Flutter is just getting started.&lt;/p&gt;

&lt;p&gt;We already have a &lt;a href="https://docs.flutter.dev/development/ui/advanced/actions_and_shortcuts"&gt;decent support for keyboard shortcuts&lt;/a&gt; or &lt;a href="https://docs.flutter.dev/development/platform-integration/windows/building#msix-packaging"&gt;deployment to stores&lt;/a&gt;. I can’t wait for &lt;a href="https://pub.dev/packages/multi_window"&gt;multi window&lt;/a&gt; support to become something built-in. We’re getting closer to rendering &lt;a href="https://github.com/flutter/flutter/issues/41722"&gt;platform views inside Flutter on desktop&lt;/a&gt;. More and more progress is &lt;a href="https://github.com/orgs/flutter/projects/20/views/1"&gt;made on desktop foundations&lt;/a&gt; such as menu bars, flavors, navigation, or input devices.&lt;/p&gt;

&lt;p&gt;Sometimes I just wonder what if in 2018 the first leap into the unknown was towards the desktop, not the web.&lt;/p&gt;

&lt;h1&gt;
  
  
  Shaders and jank issue
&lt;/h1&gt;

&lt;p&gt;This has been on my mind since I run my first iOS app in release. The &lt;a href="https://github.com/flutter/flutter/issues/813"&gt;first remarks of iOS jank issue&lt;/a&gt; are as old as the Flutter itself. Every couple of weeks I’ve been &lt;a href="https://github.com/flutter/flutter/projects/140"&gt;checking the progress on the automatic shader warm-up&lt;/a&gt; effort and was super excited to check out the new &lt;a href="https://github.com/flutter/engine/tree/main/impeller"&gt;impeller&lt;/a&gt; runtime. However, some &lt;a href="https://github.com/flutter/flutter/issues/103847"&gt;odd problems show up&lt;/a&gt; from time to time despite various improvements over the last 2 years.&lt;/p&gt;

&lt;p&gt;For those who read about it for the first time &lt;a href="https://docs.flutter.dev/perf/shader#what-is-shader-compilation-jank"&gt;here’s a quick recap&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When [the animation] shader is first used, it needs to be compiled on the device. The compilation could cost up to a few hundred milliseconds whereas a smooth frame needs to be drawn within 16 milliseconds for a 60 fps (frame-per-second) display. Therefore, a compilation could cause tens of frames to be missed, and drop the fps from 60 to 6. This is compilation jank. After the compilation is complete, the animation should be smooth.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To fix this we can precompile shaders either via automated widget tests or just manually running the app to collect the shaders before the release. Then the generated shader files can be bundled into the final build. The size increase is relatively small while the performance gains can be amazing, especially if your app relies on animations a lot. The major problem with this approach is that it needs to be redone for every new build.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Similarly to my previous more optimistic post, this list could go on much longer. I don’t want to undermine Flutter in any way, but at the same time I feel like we shouldn’t avoid speaking about some obvious problems we encounter daily. Flutter is a wonderful piece of tech, it’s no longer in its infancy. It’s a production-grade ecosystem that many companies and individuals use to build world-class experiences. It’s just a pity there are still some fundamental issues that seem to never go away…&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zNqq65-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/dash.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zNqq65-Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/dash.png" alt="" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Announcing my newsletter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To make it easier to never miss a new blog post, I decided to start a simple newsletter. It's called &lt;em&gt;Occasional Flutter&lt;/em&gt; and I invite you to subscribe today!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://occasionalflutter.substack.com"&gt;Occasional Flutter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>programming</category>
    </item>
    <item>
      <title>N things I love about Flutter</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Tue, 19 Jul 2022 14:31:33 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/n-things-i-love-about-flutter-2hfa</link>
      <guid>https://dev.to/orestesgaolin/n-things-i-love-about-flutter-2hfa</guid>
      <description>&lt;p&gt;Topics like this one may seem trivial, but frankly after using Flutter for over 4 years it's easy to forget why this tool makes me so satisfied. Thus, I decided to document some of the most spoiling things about Flutter. Maybe next time I'll write about problems I can't stand 😉&lt;/p&gt;

&lt;p&gt;This post was originally published on &lt;a href="https://roszkowski.dev/2022/thiings-i-love-about-flutter/"&gt;my personal blog&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hot reload
&lt;/h1&gt;

&lt;p&gt;This is the obvious one, but every time I have to use a &lt;em&gt;compiled&lt;/em&gt; language without this kind of instant feedback loop, I really wish instant code reload would become a new norm in any language. Recently, I gave &lt;a href="https://pub.dev/packages/dart_frog"&gt;dart_frog&lt;/a&gt; a try and the ability to instantly reload the implementation of the server code felt like magic.&lt;/p&gt;

&lt;h1&gt;
  
  
  Open Source
&lt;/h1&gt;

&lt;p&gt;Flutter and Dart are open source projects. It means you can browse and inspire from the freely accessible library of almost infinite knowledge. The IDE's ability to access the code by just inspecting it is a cherry on top.&lt;/p&gt;

&lt;p&gt;Naturally, the open source SDK code written by over a 1000 contributors is not going to be the same as client-facing production app code. You're not going to copy and paste the framework code and built the app out of that. However, anytime you need to figure out how to write an &lt;a href="https://github.com/flutter/flutter/blob/master/packages/flutter/test/services/platform_messages_test.dart"&gt;atypical test&lt;/a&gt;, build a new library of widgets, or &lt;a href="https://docs.flutter.dev/resources/design-docs"&gt;design new API used by thousands of users&lt;/a&gt; the Flutter ecosystem and codebase are places to go.&lt;/p&gt;

&lt;p&gt;The open source &lt;em&gt;attitude&lt;/em&gt; affects not only the framework code. As a developer I clearly see how it affects the community and newcomers to share all the things they build and learn every day.&lt;/p&gt;

&lt;h1&gt;
  
  
  Multi-Platform
&lt;/h1&gt;

&lt;p&gt;Since the very beginning of my work as developer I always wanted to build &lt;em&gt;for every device&lt;/em&gt;. I started with things like ASP.NET, Xamarin, Android, Ionic, or WPF. And they were really amazing tools. However, it was the Flutter that actually allowed me to not feel limited by the tech stack I use.&lt;/p&gt;

&lt;p&gt;There are definitely things that will be astronomically difficult to build with Flutter, especially on web or if the SDK does not support it yet. The recent &lt;a href="https://twitter.com/kevmoo/status/1547374258669568000"&gt;discussion on Twitter&lt;/a&gt; about Flutter Web sums it up nicely. And many of you already know my personal hesitation about this use case. However, it's all the other platforms that make me enthusiastic about the future of Flutter.&lt;/p&gt;

&lt;p&gt;Work done by Canonical with their &lt;a href="https://github.com/canonical/ubuntu-desktop-installer"&gt;Desktop Installer&lt;/a&gt;, Sonos with &lt;a href="https://tech-blog.sonos.com/posts/renovating-setup-with-flutter/"&gt;the setup sequence&lt;/a&gt;, &lt;a href="https://devblogs.microsoft.com/surface-duo/tag/flutter/"&gt;foldable support by Microsoft's Andrei Diaconu&lt;/a&gt;, &lt;a href="https://github.com/ardera/flutter-pi"&gt;Raspberry PI support&lt;/a&gt; and &lt;a href="https://github.com/slightfoot/e-ink"&gt;e-ink POC&lt;/a&gt; by Simon Lightfoot. The list could go on and on.&lt;/p&gt;

&lt;p&gt;To make it even better, the bulk of my day to day development work for mobile I can do by running a lightweight desktop app. If I need to build a simple utility app to use on macOS, most of the APIs is there and I can just reuse my mobile dev skills. If I need to dig deeper, there's another &lt;em&gt;groundbreaking&lt;/em&gt; thing about Flutter:&lt;/p&gt;

&lt;h1&gt;
  
  
  Unobstructed access to the underlying platform
&lt;/h1&gt;

&lt;p&gt;This may not be super obvious but the more you build apps that need some platform-specific functionality, the more you appreciate modular design of a Flutter project. Each platform is just another wrapper that can be freely configured as if it were a &lt;em&gt;standard&lt;/em&gt; macOS/Windows/Android/iOS/Linux... app.&lt;/p&gt;

&lt;p&gt;There are of course some specific setup steps (e.g. in Xcode build phases) that are required to run it as a Flutter app, but for the most part you can do whatever you need with your native project. This is extremely convenient compared to e.g. Xamarin where (&lt;em&gt;I guess&lt;/em&gt;) at the cost of being able to use native APIs in C#, they had to hide/bundle the native project inside of the Xamarin app.&lt;/p&gt;

&lt;p&gt;Having access to a native project means that you can utilize your other skills and resources to help you with release, customization or building more features.&lt;/p&gt;

&lt;p&gt;Especially the federated plugin architecture makes multi and cross-platform cooperation easier. In a bigger team you may find groups of experts in their own areas that alone would have a hard time building solutions in the opposite ecosystem. Imagine implementing a camera app on Android as iOS developer. With Flutter you can split the efforts with a thin layer of common "consumer-friendly" API and have the best of all worlds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--96stZp6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2022-07-19/ios-and-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--96stZp6v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2022-07-19/ios-and-diagram.png" alt="Venn diagram of iOS and Android skills that overlap to showcase Flutter skills in the middle" width="880" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;This list is definitely not complete. There are more features like thriving community of tutors and learners, high quality open source libraries (just to mention &lt;a href="https://pub.dev/packages/flutter_bloc"&gt;flutter_bloc&lt;/a&gt; and &lt;a href="https://pub.dev/packages/riverpod"&gt;riverpod&lt;/a&gt;), &lt;a href="https://discord.com/channels/608014603317936148"&gt;Discord server&lt;/a&gt; where Flutter maintainers work in the open, and many more. However, as in every technology there are also some pain points. We'll try to discuss them next time 😉&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>programming</category>
    </item>
    <item>
      <title>Flutter Animated Logo with CustomPainter</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Tue, 05 May 2020 10:20:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/flutter-animated-logo-with-custompainter-34en</link>
      <guid>https://dev.to/orestesgaolin/flutter-animated-logo-with-custompainter-34en</guid>
      <description>&lt;p&gt;FlutterLogo is a really handy widget. But what if we want to have it animated like in some of the Flutter team videos? We can try to do it with Rive, but let’s challenge ourselves and do it in pure Dart.&lt;/p&gt;

&lt;p&gt;You can check out the final result in the #FlutterPen below:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/orestesgaolin/embed/yLYVQGe?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you want, you can also read this post &lt;a href="https://roszkowski.dev/2020/flutter-animated-logo/"&gt;on my website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Implementing complex animations by breaking them down

&lt;ul&gt;
&lt;li&gt;Staggered animation&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Drawing the logo

&lt;ul&gt;
&lt;li&gt;Drawing a path&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Rotations

&lt;ul&gt;
&lt;li&gt;Rotation around arbitrary axis&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Clipping paths in Flutter&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Links&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Let’s figure out what are the main components of Flutter logo. The letter &lt;strong&gt;F&lt;/strong&gt; consists of 3 beams (2 light blue parallel and 1 dark perpendicular). There’s also a subtle shadow beneath the middle beam dropping onto the leg of the letter. It’s relatively easy to draw the shapes with Paths. We can use the original &lt;a href="https://api.flutter.dev/flutter/material/FlutterLogo-class.html"&gt;FlutterLogo&lt;/a&gt; widget as a reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HXeoZCLR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/logo_flutter_1080px_clr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HXeoZCLR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/logo_flutter_1080px_clr.png" alt="Flutter Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to check how the original FlutterLogo is implemented let’s go to the &lt;a href="https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/flutter_logo.dart"&gt;flutter_logo.dart&lt;/a&gt; file in material library. We can immediately see that it’s using AnimatedContainer’s decoration to draw logo with CustomPainter. In my proof of concept I decided that I won’t use decoration to simplify the animation process.&lt;/p&gt;

&lt;p&gt;The most useful part is available in &lt;a href="https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/painting/flutter_logo.dart#L282-L392"&gt;_paintLogo&lt;/a&gt; method of &lt;code&gt;_FlutterLogoPainter&lt;/code&gt;. It stores coordinates of the FlutterLogo that we’re going to use to draw our version of Flutter logo. Most of them will remain unchanged but we will draw the middle beam a bit differently.&lt;/p&gt;

&lt;p&gt;In original logo this part is a rotated square painted with &lt;code&gt;Colors.blue[400]&lt;/code&gt;, whereas in our case it’s going to be a result of overlaying &lt;code&gt;Colors.blue[400].withOpacity(0.8)&lt;/code&gt; onto &lt;code&gt;Colors.blue[900]&lt;/code&gt;. Take a look how it looks like during the middle beam rotation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s2WZChX_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/flutter_logo_leg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s2WZChX_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/flutter_logo_leg.gif" alt="Detail view of animated Flutter logo part"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some other small changes in the design but I won’t focus on them as most of them are really subtle (e.g. one shadow gradient instead of two as in the original design).&lt;/p&gt;

&lt;p&gt;Right now we more or less know how to draw the Flutter logo. We’re going to use CustomPainter and we’ll base our Paths on the original design and colors. But how are we going to animate it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing complex animations by breaking them down
&lt;/h2&gt;

&lt;p&gt;Whenever we try to implement some complex animations in Flutter it’s worth to apply Marcin Szalek’s principle of &lt;em&gt;breaking down complex animations into series of basic ones&lt;/em&gt;. Let’s take a look at the original Flutter logo animation available in the &lt;a href="https://drive.google.com/drive/u/0/folders/1NWJGmv_j9f0HEVWYi0NAEUPzu2l0Verp"&gt;official assets&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gP1Q1vao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/Flutter-logo-animation-v1-2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gP1Q1vao--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/Flutter-logo-animation-v1-2.gif" alt="Official Flutter logo animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see several things happening at the same time or in a sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Middle beam revealing from bottom&lt;/li&gt;
&lt;li&gt;Middle beam rotating around the lower leg with some perspective change&lt;/li&gt;
&lt;li&gt;Middle beam flipping around its longer axis when rotated by 90 degrees (this was a surprise!)&lt;/li&gt;
&lt;li&gt;Shadow animating/revealing as the middle beam is approaching the canvas plane&lt;/li&gt;
&lt;li&gt;Lower leg revealing from top&lt;/li&gt;
&lt;li&gt;Top of lower leg following the leg with a slight delay&lt;/li&gt;
&lt;li&gt;Top beam revealing from bottom&lt;/li&gt;
&lt;li&gt;Top beam and middle beam falling down onto a canvas (slight size change)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It’s almost impossible to keep track of all of these animations in your head so we need to extract them and write down all the timing parameters. Fortunately you can use some tools to split GIFs into separate frames and analyze them manually. On macOS you can just open the GIF in default Preview app to see all the frames one by one.&lt;/p&gt;

&lt;p&gt;Now the only thing to do is to take a piece of paper and write down each frame numbers of each part. Easy, isn’t it? (You can check out my parameters in the source code)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EiTCi10K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/gif_preview.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EiTCi10K--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/gif_preview.png" alt="GIF preview in macOS Image Preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Staggered animation
&lt;/h3&gt;

&lt;p&gt;After meticulous analysis of the animation we can start preparing some tweens to provide them to the CustomPainter. After creating all of them it may be a good idea to extract it to separate class (e.g. FlutterLogoController) because right now it may be hard to separate code related to animation from widgets’ code.&lt;/p&gt;

&lt;p&gt;I assumed that the logo animation will occupy 60% of the whole AnimationController. It will be equivalent to the 140 frames of the GIF above. For instance the top beam clip is one of the last animations. We can describe the clip position by a single variable.&lt;/p&gt;

&lt;p&gt;By using &lt;code&gt;Interval&lt;/code&gt; as curve for &lt;code&gt;CurvedAnimation&lt;/code&gt; we can select only the desired interval (frames 100-140). Let’s use &lt;code&gt;Curves.easeOutCubic&lt;/code&gt; to make its dynamics similar to the original animation.&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;//frames 100-140&lt;/span&gt;
&lt;span class="n"&gt;topBeamClip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Tween&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nl"&gt;begin:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;end:&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;CurvedAnimation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;parent:&lt;/span&gt; &lt;span class="n"&gt;animationController&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;curve:&lt;/span&gt; &lt;span class="n"&gt;Interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;animLimit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;animLimit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;curve:&lt;/span&gt; &lt;span class="n"&gt;Curves&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;easeOutCubic&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just repeat this for all the parameters of the animation like opacity, rotation, clipping…&lt;/p&gt;

&lt;p&gt;Having all the necessary timing and size parameters let’s try to draw the logo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drawing the logo
&lt;/h2&gt;

&lt;p&gt;We’re going to use a single CustomPainter. In Flutter it’s extremely easy to start drawing custom shapes and paths. Just create new class that extends CustomPainter and overwrite two methods: &lt;code&gt;paint(Canvas canvas, Size size)&lt;/code&gt; and &lt;code&gt;shouldRepaint(CustomPainter oldDelegate)&lt;/code&gt;. We want CustomPainter to redraw animation on each frame so let’s return true from &lt;code&gt;shouldRepaint&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnimatedLogoPainter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;CustomPainter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;paint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// here we will draw our logo&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;shouldRepaint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CustomPainter&lt;/span&gt; &lt;span class="n"&gt;oldDelegate&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First let’s position and scale our canvas so that the logo is always in the center. From the original source code we know that logo’s coordinate space is 166x202 px. The logo should be translated by a margin so that it’s centered in x axis (this distance is &lt;code&gt;(202.0 - 166.0) / 2.0&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;paint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Canvas&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Size&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTWH&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Coordinate space is 166x202 px&lt;/span&gt;
  &lt;span class="c1"&gt;// so we transform canvas and place it in the middle&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scale&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;202.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;202.0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Next, offset it some more so that the 166 horizontal pixels are centered&lt;/span&gt;
  &lt;span class="c1"&gt;// in that square (as opposed to being on the left side of it). This means&lt;/span&gt;
  &lt;span class="c1"&gt;// that if we draw in the rectangle from 0,0 to 166,202, we are drawing in&lt;/span&gt;
  &lt;span class="c1"&gt;// the center of the given rect.&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;translate&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="mf"&gt;202.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;166.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that I’m using &lt;code&gt;canvas.save()&lt;/code&gt; method that saves the current canvas operations until the corresponding &lt;code&gt;canvas.restore()&lt;/code&gt;. It’s useful if you want to apply several operations (e.g. translation, rotation, clipping) to a selected part/layer of the painting; Each &lt;code&gt;canvas.save()&lt;/code&gt; must have closing &lt;code&gt;canvas.restore()&lt;/code&gt; method. You can nest them to apply operations to subparts of your canvas and in general I recommend to save a snapshot before drawing each new part of the image. This allows to mentally “separate” operations done to given path or shape.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drawing a path
&lt;/h3&gt;

&lt;p&gt;In this article let’s just focus on one part of the Flutter logo. You can always check out the source code to see how all of the parts were implemented.&lt;/p&gt;

&lt;p&gt;Let’s see how the middle beam was drawn. It’s the most interesting part and we’ll see how it was animated later in the article.&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;lightPaint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Paint&lt;/span&gt;&lt;span class="o"&gt;()..&lt;/span&gt;&lt;span class="na"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;withOpacity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="n"&gt;middleBeam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;moveTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;156.2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;94.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;94.0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;51.6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;142.8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;79.5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;170.7&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drawPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleBeam&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lightPaint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used known coordinates to draw a path and then painted it with &lt;code&gt;lightPaint&lt;/code&gt; that by default is filling the bounded area with defined color. The path is closed automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iv-dqYLc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/middle_bar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iv-dqYLc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/middle_bar.png" alt="Middle beam of Flutter logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotations
&lt;/h2&gt;

&lt;p&gt;The most complicated part of the Flutter logo animation was rotation of the middle beam. It’s not a simple rotation by x or y axis. Firstly, the middle beam is approaching from the bottom left. Then while the top edge is touching the bottom (dark) leg, the rotations around this edge starts. When the beam is perpendicular to the canvas plane there’s an orientation flip. Take a look at the diagram below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9tyWWxOX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/leg_flip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9tyWWxOX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://roszkowski.dev/images/2020-05-04/leg_flip.png" alt="Middle beam of Flutter logo rotation flip"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When researching ways to rotate objects on Flutter canvas I couldn’t find a way to easily manipulate it around arbitrary axis. Perhaps it’s possible by combining several transformations (e.g. firstly rotating canvas by 45 degrees and then by one of the primary axes), but I tried to figure out the exact Matrix transformation that is necessary to achieve these rotations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rotation around arbitrary axis
&lt;/h3&gt;

&lt;p&gt;I found several math equations that allow to rotate given point around arbitrary axis. The best explanation along with the source code in Java I found on &lt;a href="https://sites.google.com/site/glennmurray/Home/rotation-matrices-and-formulas"&gt;Glenn Murray’s page&lt;/a&gt;. Glenn published its Java implementation on Apache license so I decided to port one of the rotation methods to Dart. It was fairly easy to port and the packaged version is available on pub.dev.&lt;/p&gt;

&lt;p&gt;In the animation below you can see the small difference if we don’t rotate the beam around its axis when the beam is perpendicular to the canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IdVgAkIO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/flip_beam_difference.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IdVgAkIO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/flip_beam_difference.gif" alt="Middle beam of Flutter logo without and with rotation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the rotation library it’s relatively easy to flip the object. The axis of rotation is [-1.0, 1.0, 0.0] and the object is rotated around point [93.45, 128.85, 0.0] which is in the middle of beam edge. We use the rotation matrix values to transform canvas with &lt;code&gt;canvas.transform()&lt;/code&gt;. The &lt;code&gt;beamRotation&lt;/code&gt; parameter is value of Tween being used to rotate the beam around its edge.&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;beamRotation&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// flip the middle beam when on the left side (i.e. rotation angle larger that 90 degrees)&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;rot2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RotationMatrix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;93.45&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;128.85&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;mtx2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rot2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMatrix&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mtx2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s focus on rotating the middle beam around its edge. The code for that is even simpler but makes a lot of difference:&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;rot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;RotationMatrix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;79.5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;170.7&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beamRotation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;mtx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMatrix&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mtx&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the axis is [1.0, 1.0, 0.0] and the rotation point is [79.5, 170.7, 0.0] which is one of the corners that stay in the sample place during the rotation. We supply the &lt;code&gt;beamRotation&lt;/code&gt; parameters which changes from Pi to 0 during the animation:&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;//frames 63-102, modified&lt;/span&gt;
&lt;span class="n"&gt;beamRotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Tween&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nl"&gt;begin:&lt;/span&gt; &lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pi&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;end:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;CurvedAnimation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;parent:&lt;/span&gt; &lt;span class="n"&gt;animationController&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;curve:&lt;/span&gt; &lt;span class="n"&gt;Interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="mi"&gt;63&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;animLimit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//tf is total animation frame count&lt;/span&gt;
      &lt;span class="mi"&gt;110&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;tf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;animLimit&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//animLimit is interval of the animation taken by this tween&lt;/span&gt;
      &lt;span class="nl"&gt;curve:&lt;/span&gt; &lt;span class="n"&gt;Curves&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;easeInOutQuad&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="o"&gt;),&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see how the animation would look like &lt;strong&gt;without&lt;/strong&gt; this rotation. Strange, isn’t it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LzzDNen5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/no_rotation.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LzzDNen5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/no_rotation.gif" alt="Middle beam of Flutter logo without rotation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the animation above you can also notice all the other small changes applied to the middle beam during its animation. The top corners are offset additionally to fake the perspective change and the whole element is scaled a bit when the animation finishes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clipping paths in Flutter
&lt;/h2&gt;

&lt;p&gt;Very important part of the Flutter logo animation is clipping. The movement is much more dynamic thanks to approaching and revealing animations of the 3 beams.&lt;/p&gt;

&lt;p&gt;I used several clipping paths and rectangles that are animated with separate tweens. In the CodePen you can enable drawing the clipping paths in the options menu. Take a look at the gif below and see 3 of them (red, green and yellow).&lt;/p&gt;

&lt;p&gt;Each of them corresponds to different element (layer) of the logo. The object is drawn only if it’s within the bounds of the clipping path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ciVZo9W6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/clipping_paths.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ciVZo9W6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://roszkowski.dev/images/2020-05-04/clipping_paths.gif" alt="Clipping paths animated"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One important remark is that if you want to clip the object with custom path, you have to call &lt;code&gt;canvas.clipPath()&lt;/code&gt; before drawing the path. In the snippet below you can see how the middle path was clipped with &lt;code&gt;clippingPath&lt;/code&gt;. Note that there are variables used that change based on the animation progress (&lt;code&gt;distance&lt;/code&gt;, &lt;code&gt;xDistance&lt;/code&gt;). Take a look at the source code for more information.&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;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="n"&gt;middleBeam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;moveTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
      &lt;span class="mf"&gt;156.2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;topCornerPerspectiveOffset&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;94.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;topCornerPerspectiveOffset&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;100.4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bottomCornerPerspectiveOffset&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="mf"&gt;94.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;topCornerPerspectiveOffset&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;51.6&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;142.8&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;79.5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;170.7&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;clippingPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;moveTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;79.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;170.7&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;107.4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;xDistance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alongMargin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="mf"&gt;142.8&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;xDistance&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;alongMargin&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;79.5&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;xDistance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alongMargin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="mf"&gt;114.9&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;xDistance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;alongMargin&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="na"&gt;lineTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;51.6&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;142.8&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;margin&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clipPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;clippingPath&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drawPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;middleBeam&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lightPaint&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;restore&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take note that I added some manual adjustments for &lt;em&gt;fake perspective&lt;/em&gt; with &lt;code&gt;topCornerPerspectiveOffset&lt;/code&gt; and &lt;code&gt;bottomCornerPerspectiveOffset&lt;/code&gt;. This was done by eye-balling the correct behavior of the shape when falling down onto canvas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;After several days I was more or less satisfied with the result. There are still some spots that could be improved and the general code quality could be better (e.g. extracting animations to separate custom controller, not recalculating some invariant animation properties on each frame).&lt;/p&gt;

&lt;p&gt;If you reached this part of the article I would like to thank you and suggest that you try similar challenge for yourself. Announcement of CodePen support for Flutter opened new range of creative possibilities that previously were available only for people with own websites. After couple of weeks we can see dozens of awesome Flutter designs being published. Maybe yours will be next?&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/orestesgaolin/animated_flutter_logo"&gt;this animation source code on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/orestesgaolin/pen/yLYVQGe"&gt;this animation on CodePen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pub.dev/packages/matrix_rotate_arbitrary_axis"&gt;matrix_rotate_arbitrary_axis&lt;/a&gt; - library allowing to rotate point around arbitrary matrix&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sites.google.com/site/glennmurray/Home/rotation-matrices-and-formulas"&gt;Glenn Murray’s article&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/flutter/announcing-codepen-support-for-flutter-bb346406fe50"&gt;CodePen support for Flutter announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/orestesgaolin"&gt;my profile on CodePen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://twitter.com/OrestesGaolin"&gt;my profile on Twitter&lt;/a&gt; - follow to be notified about new Flutter challenges&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>animation</category>
      <category>codepen</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Planning to release new mobile app? Here are some hints before you click PUBLISH.</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Tue, 31 Mar 2020 10:24:02 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/planning-to-release-new-mobile-app-here-are-some-hints-before-you-click-publish-12l2</link>
      <guid>https://dev.to/orestesgaolin/planning-to-release-new-mobile-app-here-are-some-hints-before-you-click-publish-12l2</guid>
      <description>&lt;p&gt;Here are some of my thoughts after publishing few apps both to AppStore and Google Play over several last months. If you're at the beginning of your developer journey, then it's the guide for you.&lt;/p&gt;

&lt;p&gt;The list is taken and redacted &lt;a href="https://twitter.com/OrestesGaolin/status/1244547909493960704"&gt;from my tweet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;1️⃣ First of all, if you're new developer and it's your first app prepare yourself for a long wait. First review for a new account may be longer both for iOS and Android. On Google Play your app may stay in the "extended review" for couple of days.&lt;/p&gt;

&lt;p&gt;2️⃣ Remember to secure the keystore you're using to sign the Android app. You won't be able to use different one after you upload the first release. People really often lose them in unexplained circumstances 🤯&lt;/p&gt;

&lt;p&gt;3️⃣ Add some crash reporting to your app. It may be &lt;a href="https://firebase.google.com/products/crashlytics"&gt;Crashlytics&lt;/a&gt; or any other reliable tool. Depending on the popularity you'll see dozens of crashes on some weird devices in the first days. Some of them may be called e.g. &lt;em&gt;Batmobile&lt;/em&gt; 🦇(these are bots) but other may be really popular devices.&lt;/p&gt;

&lt;p&gt;4️⃣ Consider to automate creation of your screenshots. Maybe implement some mock repository to always get the same data? Or use UI tests and run them on several devices? Depending on the complexity at some point you will detest recreating screenshots manually.&lt;/p&gt;

&lt;p&gt;5️⃣ Depending on your attitude to privacy consider adding some kind of analytics. The store dashboards are enough at the beginning, but sooner or later you will want to understand your users better. Some of the state of the art are &lt;a href="https://firebase.google.com/docs/analytics"&gt;Firebase Analytics&lt;/a&gt;, &lt;a href="https://mixpanel.com/"&gt;Mixpanel&lt;/a&gt;, &lt;a href="https://clevertap.com/"&gt;CleverTap&lt;/a&gt;, &lt;a href="https://www.flurry.com/"&gt;Flurry&lt;/a&gt;...&lt;/p&gt;

&lt;p&gt;6️⃣ The first 50-100 users of your app may be bots e.g. from Google Play pre-launch report or apk aggregators. At some point you may want to &lt;a href="https://support.google.com/googleplay/android-developer/answer/7002270?hl=en"&gt;disable pre-launch report&lt;/a&gt; because of its impact on analytics.&lt;/p&gt;

&lt;p&gt;7️⃣ Prepare the privacy policy. If you are complete newbie try to understand what your privacy policy should cover. There are some automated tools to help you create the one for your app. Create separate e-mail alias or account just for this app.&lt;/p&gt;

&lt;p&gt;8️⃣ Think about versioning scheme of your app. I highly recommend to use tags in your git repository to have clear understanding of what's in a given release. Maybe start a changelog? &lt;/p&gt;

&lt;p&gt;Show the app version label in your app to make error reporting easier.&lt;/p&gt;

&lt;p&gt;9️⃣ Remove unused fonts and other assets from your app. Create adaptive icon on Android if you haven't done it yet.&lt;/p&gt;

&lt;p&gt;🔟 Think if you need some kind of non-intrusive force update mechanism. In case of some nasty bug you'll be able to upgrade your users faster.&lt;/p&gt;

&lt;p&gt;1️⃣1️⃣ Consider a polite way of requesting user rating. Maybe show a snackbar after 5 consecutive launches? Or ask for review after successful process? Or at least add a button in your settings. You will be over the moon through the first 10-20 reviews 😄&lt;/p&gt;

&lt;p&gt;🏁 And finally tell your friends and family about your new app. It's hard to reach first 20-50 users and they will definitely help you.  &lt;/p&gt;

&lt;p&gt;Good luck with your adventure 🎉. Feel free to share your advices if you have some!&lt;/p&gt;




&lt;p&gt;You can also read this as &lt;a href="https://twitter.com/OrestesGaolin/status/1244547909493960704"&gt;a Twitter thread&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>flutter</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Convenient internationalization of Flutter apps</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Tue, 25 Feb 2020 08:00:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/convenient-internationalization-of-flutter-apps-1gfd</link>
      <guid>https://dev.to/orestesgaolin/convenient-internationalization-of-flutter-apps-1gfd</guid>
      <description>&lt;p&gt;There are many ways to have multiple languages in your app in Flutter. Some folks use canonical Flutter &lt;strong&gt;arb&lt;/strong&gt; files. Other use some json equivalents or statically typed constant resources like r_flutter. In this post I’m going to show you the way I find the most convenient to setup and use.&lt;/p&gt;

&lt;p&gt;See the short video version:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3IRGTTjVHug"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There's also &lt;a href="https://roszkowski.dev/2020/i18n-in-flutter/"&gt;original post on my blog&lt;/a&gt; if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Foreword
&lt;/h2&gt;

&lt;p&gt;While researching the i18n[^1] topic I found several different ways of adding multiple languages support in Flutter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://flutter.dev/docs/development/accessibility-and-localization/internationalization"&gt;Canonical Flutter way with manual configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2"&gt;Using json files and custom localization delegate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Recent semi-automatic proposal of &lt;a href="https://blog.codemagic.io/localising-flutter-applications-and-automating-the-localisation-process/"&gt;downloading arb files with some custom scripts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/r_flutter"&gt;Generating constant resources with r_flutter package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Some other fairly popular localization techniques:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/flutter_translate"&gt;flutter_translate&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/easy_localization"&gt;easy_localization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pub.dev/packages/ez_localization"&gt;ez_localization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;dynamic &lt;a href="https://pub.dev/packages/auto_localization"&gt;auto_localization&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;and perhaps several others&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see there’s no single best way to handle this right now. All of them share some common steps, though. Together with our colleagues we also have been using a custom localization solution with some bash scripts to fetch and convert localization json files. Unfortunately this causes delays when onboarding new people and starting new projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My goal was to find a solution that:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is relatively quick to setup in new project (either is single time manual setup or fully automatic),&lt;/li&gt;
&lt;li&gt;supports {placeholders} and preferably plural forms,&lt;/li&gt;
&lt;li&gt;would be nice to rely on &lt;strong&gt;arb&lt;/strong&gt; files which are native to Flutter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After few days of hacking I think I found something that I can share with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  General overview
&lt;/h2&gt;

&lt;p&gt;Simply speaking my solution is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use some tool to translate your app (POEditor, Localizely, Loco, CrowdIn…).

&lt;ul&gt;
&lt;li&gt;Not all of them support exporting &lt;strong&gt;arb&lt;/strong&gt; files. In that case export &lt;strong&gt;json&lt;/strong&gt; and convert it to &lt;strong&gt;arb&lt;/strong&gt; with &lt;a href="https://github.com/bmw-tech/arb-converter-cli"&gt;arb-converter-cli&lt;/a&gt;[^2]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Fetch translation files from API directly in VS Code (and convert them to arb if necessary)&lt;/li&gt;
&lt;li&gt;Automatically generate translation classes with &lt;a href="https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl"&gt;Flutter Intl&lt;/a&gt; extension&lt;/li&gt;
&lt;li&gt;Be happy with statically typed translations with plurals and placeholder support&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach requires some configuration steps once but later is mostly automatic due to VS Code extension and tasks in VS Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translation services and arb files
&lt;/h2&gt;

&lt;p&gt;Only few translation services support &lt;strong&gt;arb&lt;/strong&gt; files out of the box. This is the list I came up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Localizely - arb supported ✔️&lt;/li&gt;
&lt;li&gt;Crowdin - arb supported ✔️ but a bit tricky (e.g. names not changed automatically and source file has to be arb too)&lt;/li&gt;
&lt;li&gt;POEditor - not supported, but json can be converted to arb without plural support ✔️&lt;/li&gt;
&lt;li&gt;Loco - arb supported, but without placeholders and genders ✔️&lt;/li&gt;
&lt;li&gt;Lingohub - not supported&lt;/li&gt;
&lt;li&gt;Weblate - not supported&lt;/li&gt;
&lt;li&gt;Transifex - not supported&lt;/li&gt;
&lt;li&gt;Pootle - not supported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also some standalone editors like &lt;a href="https://jamesblasco.github.io/arb_editor/master/#/"&gt;this web-based&lt;/a&gt; (without plurals support) and &lt;a href="https://www.codeandweb.com/babeledit"&gt;BabelEdit&lt;/a&gt; which is a desktop editor.&lt;/p&gt;

&lt;p&gt;In my case the most convenient to use was POEditor, but the json file couldn’t be easily converted to arb[^1]. Thus, I stayed with Localizely for now. Both have free plans that should be enough for smaller apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code/Android Studio extension - Flutter Intl
&lt;/h2&gt;

&lt;p&gt;One of the best tools to streamline internationalization of Flutter apps is &lt;a href="https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl"&gt;Flutter Intl&lt;/a&gt; extension (also available for &lt;a href="https://plugins.jetbrains.com/plugin/13666-flutter-intl"&gt;Android Studio&lt;/a&gt;). It generates the boilerplate getters for translation keys. It will also generate special methods if translation term has &lt;strong&gt;placeholder&lt;/strong&gt; or &lt;strong&gt;plural&lt;/strong&gt; version. Unfortunately, it’s closed source.&lt;/p&gt;

&lt;p&gt;The setup is pretty straightforward and I recommend you to read the README.&lt;/p&gt;

&lt;p&gt;In short, if you have enabled Flutter Intl extension, then you just put your arb files in l10n directory and the extension generates Dart files for you. The extension automatically updates available locales and generates localization delegate. Then you can reference translation terms in you widgets via:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Text(
    S.of(context).page_home_counter(_counter),
    // 'You have pushed the button {count} times',
),

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And if you take a look at the source of the generated method you’ll see typical Flutter &lt;code&gt;intl&lt;/code&gt; package handling:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String page_home_counter(dynamic count) {
    return Intl.plural(
        count,
        one: 'You have pushed the button once',
        other: 'You have pushed the button $count times',
        name: 'page_home_counter',
        desc: '',
        args: [count],
    );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Fetching translation updates with VS Code
&lt;/h2&gt;

&lt;p&gt;VS Code allows to set custom tasks which are arbitrary shell commands. That’s what I wanted to utilize to semi-automatically fetch translation files from Localizaly or POEditor API. I prepared simple bash script to download arb files from Localizely and json files from POEditor. You can take a look at the tasks.json file in this gist if you want to use them from VS Code. The script is available &lt;a href="https://github.com/orestesgaolin/i18n_example_flutter/blob/master/scripts/localizely.sh"&gt;here&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I hope this short tutorial helped you to improve your internationalization workflow. The arb files are very powerful, but at the same time almost no translation services support it. There is &lt;a href="https://github.com/flutter/flutter/issues/41437"&gt;one issue&lt;/a&gt; in the Flutter GitHub project that holds the discussion about the simplified i18n process. I really recommend you to read it.&lt;/p&gt;

&lt;p&gt;The app code and download script are available in &lt;a href="https://github.com/orestesgaolin/i18n_example_flutter"&gt;the project repository&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;[1^]: i18n stands for internationalization which is just adding multiple languages to the app, whereas l12n stands for localization which is adapting your app to given locale/culture/region (e.g. different layout or navigation).&lt;/p&gt;

&lt;p&gt;[2^]: Unfortunately json conversion works for very simple terms without plural version but works OK with placeholders.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>i18n</category>
      <category>l10n</category>
      <category>arb</category>
    </item>
    <item>
      <title>Using custom curve in Flutter Hero animations</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Sat, 21 Sep 2019 23:00:00 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/using-custom-curve-in-flutter-hero-animations-4bom</link>
      <guid>https://dev.to/orestesgaolin/using-custom-curve-in-flutter-hero-animations-4bom</guid>
      <description>&lt;p&gt;Sometimes the default Hero behavior is not enough and you’d like to add some custom taste to it. One of the simple ways to do it is to customize curve of the transition animation.&lt;/p&gt;

&lt;p&gt;In this post you’ll see a sample that allows to use any custom curve to animate bounds of the Hero. In my case it was a bit faster expansion in vertical axis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Froszkowski.dev%2Fimages%2Fhero.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Froszkowski.dev%2Fimages%2Fhero.gif" alt="Comparison"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hero properties&lt;/li&gt;
&lt;li&gt;What is createRectTween&lt;/li&gt;
&lt;li&gt;How to change the default implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Hero properties
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://api.flutter.dev/flutter/widgets/Hero-class.html" rel="noopener noreferrer"&gt;Hero&lt;/a&gt; class has several convenient properties that allow to customize its behavior. These are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;flightShuttleBuilder&lt;/code&gt; that allows to wrap or change entire Hero animation by e.g. adding rotation or fade transition&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;placeholderBuilder&lt;/code&gt; that gives us possibility to show Widget in place previously occupied by the animated widget&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;createRectTween&lt;/code&gt; which is a function that determines the bounds of the Hero during animation - that’s what we want to customize today&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What is createRectTween
&lt;/h1&gt;

&lt;p&gt;This property takes a &lt;code&gt;CreateRectTween&lt;/code&gt; function which is simply a typedef for &lt;code&gt;Tween&amp;lt;Rect&amp;gt; Function(Rect begin, Rect end)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It means that it interpolates two Rects between &lt;code&gt;begin&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; state. By default a &lt;code&gt;RectTween(begin: begin, end: end)&lt;/code&gt; is used in Hero. It uses its &lt;a href="https://api.flutter.dev/flutter/dart-ui/Rect/lerp.html" rel="noopener noreferrer"&gt;lerp&lt;/a&gt; method which aside from few asserts only interpolates Rect coordinates in linear manner:&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="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="nf"&gt;lerp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&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;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTRB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;t&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;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTRB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;k&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;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTRB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&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;The method &lt;code&gt;lerpDouble&lt;/code&gt; is where the interpolation takes place:&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="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;num&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;num&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="mf"&gt;0.0&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&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;h1&gt;
  
  
  How to change the default implementation
&lt;/h1&gt;

&lt;p&gt;When defining your Hero you should return a custom &lt;code&gt;createRectTween&lt;/code&gt; function like this:&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;Hero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;tag:&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;createRectTween:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&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;CustomRectTween&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;a:&lt;/span&gt; &lt;span class="n"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;b:&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nl"&gt;child:&lt;/span&gt; &lt;span class="n"&gt;Poster&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;And finally &lt;code&gt;CustomRectTween&lt;/code&gt; has to extend &lt;code&gt;RectTween&lt;/code&gt; and to have overwritten &lt;code&gt;lerp&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;The parameter &lt;code&gt;t&lt;/code&gt; is animation clock value between 0.0 and 1.0. Each &lt;code&gt;Curve&lt;/code&gt; can be used to interpolate any given value between 0.0 and 1.0 on the curve. I use my custom cubic curve in this example.&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomRectTween&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;RectTween&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CustomRectTween&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;begin:&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;end:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Rect&lt;/span&gt; &lt;span class="n"&gt;lerp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Curves&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;elasticOut&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;//any curve can be applied here e.g. Curve.elasticOut.transform(t);&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;verticalDist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Cubic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&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="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;verticalDist&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;Rect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromLTRB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&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="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;lerpDouble&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;num&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;num&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;??=&lt;/span&gt; &lt;span class="mf"&gt;0.0&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&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;It’s important to note that I wanted to change only &lt;code&gt;top&lt;/code&gt; coordinate of the Hero’s Rect, so when calculating the value I use &lt;code&gt;(1 - verticalDist)&lt;/code&gt; (thus inverting the curve).&lt;/p&gt;

&lt;p&gt;I hope you’ll find it useful and try to experiment with Hero widget on your own.&lt;/p&gt;

&lt;p&gt;I highly recommend the post &lt;a href="https://medium.com/flutter-community/mastering-hero-animations-in-flutter-bc07e1bea327" rel="noopener noreferrer"&gt;Mastering Hero Animations in Flutter&lt;/a&gt; by Chema Molins from Flutter Community.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>hero</category>
      <category>dart</category>
      <category>animations</category>
    </item>
    <item>
      <title>Flavors in Flutter with Fastlane - yet another guide</title>
      <dc:creator>Dominik Roszkowski</dc:creator>
      <pubDate>Mon, 15 Jul 2019 19:48:57 +0000</pubDate>
      <link>https://dev.to/orestesgaolin/flavors-in-flutter-with-fastlane-yet-another-guide-1799</link>
      <guid>https://dev.to/orestesgaolin/flavors-in-flutter-with-fastlane-yet-another-guide-1799</guid>
      <description>&lt;p&gt;It's a good practice to build separate apps for development, test and production environment. In case of mobile apps a good way to have separate configurations is usage of flavors.&lt;/p&gt;

&lt;p&gt;In this tutorial you will learn how to prepare ordinary Flutter project to have 3 different flavors (&lt;strong&gt;dev&lt;/strong&gt;, &lt;strong&gt;test&lt;/strong&gt; and &lt;strong&gt;production&lt;/strong&gt;) and how to handle build, signing and deployment with fastlane.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; Just go to &lt;a href="https://github.com/orestesgaolin/flutter-flavors" rel="noopener noreferrer"&gt;the repository&lt;/a&gt; where all the flavors are already configured.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also read the tutorial without large gifs &lt;a href="https://roszkowski.dev/2019/flutter-flavors/" rel="noopener noreferrer"&gt;on my blog&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Basics
&lt;/h2&gt;

&lt;p&gt;The concept of flavors is taken from Android apps and can be applied to iOS in various ways (more on this later). By incorporating flavors in your project you can build your app with different configuration options, styles or feature sets. In commercial projects it's a standard way of distributing apps.&lt;/p&gt;

&lt;p&gt;There are several great articles on build flavors just to mention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cogitas.net/creating-flavors-of-a-flutter-app/" rel="noopener noreferrer"&gt;Creating flavors of a Flutter app (Flutter &amp;amp; Android setup)&lt;/a&gt; by Natalie Masse Hooper,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/@salvatoregiordanoo/flavoring-flutter-392aaa875f36" rel="noopener noreferrer"&gt;Flavoring Flutter&lt;/a&gt; by Salvatore Giordano,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://medium.com/flutter-community/flutter-ready-to-go-e59873f9d7de#c38c" rel="noopener noreferrer"&gt;Flutter Ready to Go (flavors, connectivity and more)&lt;/a&gt; by Julio Henrique Bitencourt. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article I'll show a similar but a subtly different approach and focus mostly on iOS part. Presented way works really well for me and my colleagues. It's been battle tested with several apps already and is getting better with each new project.&lt;/p&gt;

&lt;p&gt;For instance our test builds have AppCenter distribution packages to automate updates and additional logging included, dev builds have very verbose logging, and production apps come without unnecessary diagnostics but with production logging configuration.&lt;/p&gt;

&lt;p&gt;Flutter comes with built-in flavor support but default project is not prepared to handle them. All it takes to define flavors is to add and edit few files. There are multiple ways to achieve this and with each new project you'll have a chance to improve your approach. Especially on iOS there are multiple ways to provide different bundle ids or configuration parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fastlane
&lt;/h3&gt;

&lt;p&gt;In my daily job I use &lt;strong&gt;fastlane&lt;/strong&gt; to automate apps deployment to QA and app stores. In this article I will show how to use flavors with fastlane but in general you can handle flavors manually or in typical CI environment like Codemagic or Bitrise.&lt;/p&gt;

&lt;p&gt;Fastlane allows you to define specific &lt;em&gt;lanes&lt;/em&gt; for each app in code like &lt;code&gt;deploy_to_appcenter&lt;/code&gt; or &lt;code&gt;deploy_to_store&lt;/code&gt;. A set of files can describe signing, build and deployment phases. Those can be reproduced both on developer's computer, but also on CI/CD platform. Fastlane allows to automate provisioning and signing of iOS apps as well as screenshot capture or updating the description in store. This gives us a very convenient and reproducible way of distributing our app.&lt;/p&gt;

&lt;p&gt;There is no native support of Flutter apps in fastlane but we can define fastlane configuration for Android and iOS projects and treat them as typical native apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flavors in Dart
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Preparation
&lt;/h3&gt;

&lt;p&gt;In this article I use Flutter v1.7.8+hotfix.3 and demo app is created with Kotlin, AndroidX, and Swift support by:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh35zo8i7lx139m7v2dw9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh35zo8i7lx139m7v2dw9.png" alt="flutter create -i swift -a kotlin --androidx --org com.flutter flutter_flavors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a good practice to create new projects with Kotlin and Swift support. AndroidX is a future of Android development so while starting new project you should definitely have it enabled. You will benefit from Swift later in your project when you'll have to write some platform specific code.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to configure Flutter project
&lt;/h3&gt;

&lt;p&gt;In order to take the advantage of flavors in Flutter app you should define 3 separate main files&lt;sup id="fnref1"&gt;1&lt;/sup&gt; that will handle all the configuration details different for each scheme. The easiest way is to rename &lt;code&gt;main.dart&lt;/code&gt; to &lt;code&gt;main_common.dart&lt;/code&gt; and create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main_dev.dart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main_tst.dart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main_prod.dart&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In each of them you can define respective configuration and later just start execution of the app from a common function.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:async'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_flavors/app_config.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter_flavors/main_common.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// async can be useful if you fetch from disk or network&lt;/span&gt;
  &lt;span class="c1"&gt;// do flavor specific configuration here e.g. API endpoints&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tst'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;mainCommon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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;The &lt;code&gt;AppConfig&lt;/code&gt; class is a used to store some basic configuration options like name or API endpoints.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;main_common.dart&lt;/code&gt; you should replace the 3rd line with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;mainCommon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;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="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This step of the configuration you can investigate in &lt;a href="https://github.com/orestesgaolin/flutter-flavors/commit/a4c7ef8e351676825b422839baa539ecf7f021d9" rel="noopener noreferrer"&gt;commit a4c7ef8e&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to build or run Flutter project
&lt;/h3&gt;

&lt;p&gt;Typically you would run following commands to build flavored app:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ordinary apk:&lt;/strong&gt; &lt;code&gt;flutter build apk --release -t lib/main_tst.dart --build-name=1.0.0 --build-number=1 --flavor tst&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;App bundle:&lt;/strong&gt; &lt;code&gt;flutter build appbundle --target-platform android-arm,android-arm64 --release -t lib/main_tst.dart --build-name=1.0.0 --build-number=1 --flavor tst&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iOS:&lt;/strong&gt; &lt;code&gt;flutter build ios --release --no-codesign -t lib/main_tst.dart --build-name=1.0.0 --build-number=1 --flavor tst&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Some important things to notice here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we define build numbers (&lt;code&gt;1&lt;/code&gt;) and build names (&lt;code&gt;1.0.0&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;we use &lt;strong&gt;tst&lt;/strong&gt; flavor&lt;/li&gt;
&lt;li&gt;we skip codesign for iOS (we'll sign our app with fastlane)&lt;/li&gt;
&lt;li&gt;we'll sign our android app later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to run the app with desired flavor in VS Code you can define your own &lt;code&gt;launch.json&lt;/code&gt; configuration. Below you may find a sample I use in my apps. You may copy it to the configuration file that opens when you click the cog wheel on debug pad in VS Code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3j4lxm75q1p2ww2w9bl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd3j4lxm75q1p2ww2w9bl.png" alt="click this cog wheel in VS Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter Dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_dev.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter Dev Release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_dev.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter Profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_dev.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter Test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_tst.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"tst"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flutter Prod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lib/main_prod.dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"--flavor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"prod"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point these commands would fail because we haven't defined flavors in Android and iOS apps yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flavors on Android
&lt;/h2&gt;

&lt;p&gt;Defining flavors on Android is really straightforward. The only file to be changed is &lt;code&gt;build.gradle&lt;/code&gt; in &lt;code&gt;app&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Just add the following lines after buildTypes node and before closing bracket:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;

    &lt;span class="n"&gt;flavorDimensions&lt;/span&gt; &lt;span class="s2"&gt;"flavor-type"&lt;/span&gt;

    &lt;span class="n"&gt;productFlavors&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"flavor-type"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s2"&gt;".dev"&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-dev"&lt;/span&gt;
            &lt;span class="n"&gt;manifestPlaceholders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;appName:&lt;/span&gt; &lt;span class="s2"&gt;"Flavor DEV"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;tst&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"flavor-type"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s2"&gt;".test"&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-test"&lt;/span&gt;
            &lt;span class="n"&gt;manifestPlaceholders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;appName:&lt;/span&gt; &lt;span class="s2"&gt;"Flavor TST"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;prod&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="s2"&gt;"flavor-type"&lt;/span&gt;
            &lt;span class="n"&gt;manifestPlaceholders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nl"&gt;appName:&lt;/span&gt; &lt;span class="s2"&gt;"Flavor"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can take a look at the exact diff here in &lt;a href="https://github.com/orestesgaolin/flutter-flavors/commit/cef5fbffea052c9c26f93aa4ab63d3434b0a44e1" rel="noopener noreferrer"&gt;commit cef5fbff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some important notes here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we have different app ids for each flavor: &lt;code&gt;com.flutter.flutter_flavor.dev&lt;/code&gt;, &lt;code&gt;com.flutter.flutter_flavor.test&lt;/code&gt;, and &lt;code&gt;com.flutter.flutter_flavor&lt;/code&gt; - this way you can install all 3 apps on a single device, have separate google-services.json files and distinct the app in some logging service or Firebase&lt;/li&gt;
&lt;li&gt;we set different app names&lt;/li&gt;
&lt;li&gt;we set different version name suffixes e.g. &lt;code&gt;1.0.0&lt;/code&gt; becomes &lt;code&gt;1.0.0-test&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Froszkowski.dev%2Fimages%2F2019-07-15-flutter-guide-flavors%2Fandroid-res.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Froszkowski.dev%2Fimages%2F2019-07-15-flutter-guide-flavors%2Fandroid-res.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Flavors on Android allow us to define separate resources for each of them. E.g. you can have a special icon for QA builds or different strings resources.&lt;br&gt;
What you need to do to provide new icon is just create &lt;code&gt;mipmap-...&lt;/code&gt; folders with icons in &lt;code&gt;app/src/tst&lt;/code&gt; directory. The same works for any other resources and schemes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

app
| - src
   | - debug &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;
   | - main &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;
   | - profile &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;
   | - tst &lt;span class="o"&gt;(&lt;/span&gt;add this with desired subdirectories&lt;span class="o"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point you should be able to build 3 separate flavors of the app for Android.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flavors on iOS
&lt;/h2&gt;

&lt;p&gt;Typically, in iOS apps you can base flavors on build schemes. In order to configure this you'll need macOS and Xcode. To start you should open &lt;code&gt;ios/Runner.xcworkspace&lt;/code&gt; in Xcode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13td4ibe38i9rfksckbh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13td4ibe38i9rfksckbh.png" alt="iOS project open in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating schemes
&lt;/h3&gt;

&lt;p&gt;Default scheme for Flutter apps is Runner. We'll define additional 3 schemes named exactly as the previously defined flavors i.e. &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;tst&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt;. You should go to &lt;code&gt;Product &amp;gt; Scheme &amp;gt; Manage&lt;/code&gt; schemes and add them via &lt;code&gt;+&lt;/code&gt; button. Make sure the schemes are marked as &lt;em&gt;Shared&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ruaju8bh5w2u9azmop9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ruaju8bh5w2u9azmop9.gif" alt="creating iOS schemes in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://roszkowski.dev/assets/images/ios-xcode-scheme.mp4" rel="noopener noreferrer"&gt;mp4&lt;/a&gt;/&lt;a href="https://roszkowski.dev/assets/images/ios-xcode-scheme.webm" rel="noopener noreferrer"&gt;webm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you should add 3 xconfig files to Flutter directory next to Debug, Release and Generated. Right click on Flutter directory on left pad in Xcode and select &lt;code&gt;New File&lt;/code&gt;. Select &lt;code&gt;Configuration Settings File&lt;/code&gt; and add &lt;code&gt;dev.xconfig&lt;/code&gt;, &lt;code&gt;tst.xconfig&lt;/code&gt; and &lt;code&gt;prod.xconfig&lt;/code&gt;. Make sure they're in Flutter directory as seen on the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhw8nw5mu7mf4p8y402n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhw8nw5mu7mf4p8y402n.png" alt="iOS configuration files"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These files allow you to define custom variables that can be used later during build or in &lt;code&gt;Info.plist&lt;/code&gt; file. We'll define our custom app bundle ids here.&lt;/p&gt;

&lt;p&gt;My typical &lt;code&gt;dev.xconfig&lt;/code&gt; files look like follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"Generated.xcconfig"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="n"&gt;BUNDLE_ID_SUFFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;span class="n"&gt;PRODUCT_BUNDLE_IDENTIFIER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flutter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flutterflavors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;
&lt;span class="n"&gt;FLUTTER_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main_dev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dart&lt;/span&gt;
&lt;span class="n"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Flavor&lt;/span&gt; &lt;span class="n"&gt;DEV&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;and &lt;code&gt;tst.xconfig&lt;/code&gt; (note &lt;code&gt;.test&lt;/code&gt; suffix, not &lt;code&gt;.tst&lt;/code&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"Generated.xcconfig"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="n"&gt;BUNDLE_ID_SUFFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;PRODUCT_BUNDLE_IDENTIFIER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flutter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flutterflavors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;
&lt;span class="n"&gt;FLUTTER_TARGET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;main_tst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dart&lt;/span&gt;
&lt;span class="n"&gt;APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Flavor&lt;/span&gt; &lt;span class="n"&gt;TST&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Extending configuration
&lt;/h3&gt;

&lt;p&gt;At this point you should copy and paste some build configurations and assign them to the respective scheme. There will be a lot of clicking and typing now so be patient.&lt;/p&gt;

&lt;p&gt;Go to project settings in Xcode, select &lt;code&gt;Runner&lt;/code&gt; and then &lt;code&gt;Debug&lt;/code&gt; in &lt;em&gt;Configurations&lt;/em&gt; section. Press Enter to rename it to &lt;code&gt;Debug-dev&lt;/code&gt;. Then duplicate it and call it &lt;code&gt;Debug-tst&lt;/code&gt;, and another with &lt;code&gt;Debug-prod&lt;/code&gt;. Repeat the procedure for &lt;code&gt;Release&lt;/code&gt; and &lt;code&gt;Profile&lt;/code&gt; configurations. Then assign previously created schemes to respective configurations. You should end up with following layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3ywyyqrxkqjy6x96qk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg3ywyyqrxkqjy6x96qk7.png" alt="iOS Xcode configurations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This should allow you to build your app with different bundle id per flavor. To make sure you can go to Build Settings of Runner target and look for &lt;code&gt;Product Bundle Identifier&lt;/code&gt; position.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52sbumvaqfi6xmwu9ptg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F52sbumvaqfi6xmwu9ptg.png" alt="iOS product bundle identifiers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is still one problem to be solved. When building the app Flutter takes into account the product bundle identifier visible in &lt;em&gt;General&lt;/em&gt; tab of the target properties. So even with &lt;code&gt;tst&lt;/code&gt; flavor you'll see following output in console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdr6u89n5zamrla0chcuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdr6u89n5zamrla0chcuj.png" alt="flutter build ios --release --no-codesign -t lib/main_tst.dart --build-number=1 --build-name=1.0.0 --flavor tst"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the wrong bundle id for &lt;code&gt;Release-tst&lt;/code&gt; scheme.&lt;/p&gt;

&lt;p&gt;Fortunately, if we defined a &lt;code&gt;PRODUCT_BUNDLE_IDENTIFIER&lt;/code&gt; variable in our &lt;code&gt;tst.xconfig&lt;/code&gt; file this will be overwritten during the build so that generating and signing the &lt;code&gt;.test&lt;/code&gt; bundle id will be possible.&lt;/p&gt;
&lt;h3&gt;
  
  
  Archiving
&lt;/h3&gt;

&lt;p&gt;Finally, we should update each build scheme with correct build configuration.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;Product &amp;gt; Schemes &amp;gt; Manage Schemes&lt;/code&gt;, select &lt;code&gt;dev&lt;/code&gt; and click &lt;code&gt;Edit&lt;/code&gt;. Now for each of the processes (Run, Test, Profile, Analyze, Archive) change the build configuration to &lt;code&gt;-dev&lt;/code&gt; one. Repeat the process for &lt;code&gt;tst&lt;/code&gt; and &lt;code&gt;prod&lt;/code&gt; schemes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mrmpad45rzhn49992xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3mrmpad45rzhn49992xj.png" alt="iOS build configs updated"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Signing iOS app with fastlane
&lt;/h2&gt;

&lt;p&gt;In order to sign and provision your app you'll need Apple developer account and fastlane configured. I recommend creating a separate 'service' account for fastlane only with separate certificate. Create 3 application identifiers in Apple Developer portal e.g. &lt;code&gt;com.flutter.flutterflavors&lt;/code&gt;, &lt;code&gt;com.flutter.flutterflavors.test&lt;/code&gt;, and &lt;code&gt;com.flutter.flutterflavors.dev&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;ios&lt;/code&gt; folder in your console and initialize fastlane with manual mode (option 4.). In fastlane folder create &lt;code&gt;Matchfile&lt;/code&gt; file next to &lt;code&gt;Fastfile&lt;/code&gt; and &lt;code&gt;Appfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My typical &lt;code&gt;Matchfile&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# you should store your provisioning profiles and certs in repository&lt;/span&gt;
&lt;span class="c1"&gt;# this repository is encrypted with MATCH_PASSWORD env variable&lt;/span&gt;
&lt;span class="n"&gt;git_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FASTLANE_GIT"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;storage_mode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FASTLANE_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;team_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FASTLANE_TEAM"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;# this is useful on CI/CD if you build test and production app &lt;/span&gt;
&lt;span class="c1"&gt;# flavors with the same steps configuration&lt;/span&gt;
&lt;span class="n"&gt;app_identifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"APP_NAME"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After creating application ids and adding the files you should be able to generate provisioning profiles. Execute following commands and type desired bundle id when prompted:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

bundle exec fastlane match development
bundle exec fastlane match adhoc
bundle exec fastlane match release


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This whole iOS step can be observed in &lt;a href="https://github.com/orestesgaolin/flutter-flavors/commit/162d2015cdab401fb1eafa8e06da88fa71351e16" rel="noopener noreferrer"&gt;commit 162d2015&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebuilding and signing with fastlane
&lt;/h3&gt;

&lt;p&gt;Unfortunately, it is necessary to rebuild iOS app to archive it and sign before deploying to testers or AppStore&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. With custom flavors it is necessary to provide provisioning profile match map manually. I couldn't make the fastlane to detect all profiles automatically. If anyone knows better way to do this, then please share!&lt;/p&gt;

&lt;p&gt;My typical &lt;code&gt;Fastfile&lt;/code&gt; for QA/test builds looks as follows&lt;sup id="fnref4"&gt;4&lt;/sup&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# update_fastlane&lt;/span&gt;

&lt;span class="n"&gt;default_platform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:ios&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;platform&lt;/span&gt; &lt;span class="ss"&gt;:ios&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Submit a new build to AppCenter"&lt;/span&gt;
  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# add_badge(dark: true)&lt;/span&gt;
    &lt;span class="n"&gt;register_devices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="ss"&gt;devices_file: &lt;/span&gt;&lt;span class="s2"&gt;"fastlane/devices.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;team_id: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FASTLANE_TEAM"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;username: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FASTLANE_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;type: &lt;/span&gt;&lt;span class="s2"&gt;"adhoc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;force_for_new_devices: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;automatic_code_signing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;use_automatic_signing: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;update_project_provisioning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;profile: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sigh_com.flutter.flutterflavors.test_adhoc_profile-path"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="ss"&gt;build_configuration: &lt;/span&gt;&lt;span class="s2"&gt;"Release-tst"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;code_signing_identity: &lt;/span&gt;&lt;span class="s2"&gt;"iPhone Distribution"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;build_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;scheme: &lt;/span&gt;&lt;span class="s2"&gt;"tst"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;configuration: &lt;/span&gt;&lt;span class="s2"&gt;"Release-tst"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;xcargs: &lt;/span&gt;&lt;span class="s2"&gt;"-allowProvisioningUpdates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;export_options: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;signingStyle: &lt;/span&gt;&lt;span class="s2"&gt;"manual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;method: &lt;/span&gt;&lt;span class="s2"&gt;"ad-hoc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;provisioningProfiles: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"com.flutter.flutterflavors.test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"match AdHoc com.flutter.flutterflavors.test"&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="ss"&gt;output_name: &lt;/span&gt;&lt;span class="s2"&gt;"Runner.ipa"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# upload to AppCenter or anywhere else&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="s2"&gt;"Deploy a new version to the AppStore"&lt;/span&gt;
  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:prod&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To build the app with fastlane you should execute just:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

bundle exec fastlane ios test


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;At this point you may encounter a very nasty error that fastlane tries to build &lt;code&gt;com.flutter.flutterflavors.dev&lt;/code&gt; instead of &lt;code&gt;com.flutter.flutterflavors.test&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4yokba2msyzsw8kma0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4yokba2msyzsw8kma0v.png" alt="❌ error: Provisioning profile “match AdHoc com.flutter.flutterflavors.test” has app ID “com.flutter.flutterflavors.test”, which does not match the bundle ID “com.flutter.flutterflavors.dev”. (in target ‘Runner’)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The simplest solution that took me hours to find was just to delete bundle id from General tab in Xcode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ddzhghov8hv3849r9iq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ddzhghov8hv3849r9iq.gif" alt="iOS Xcode delete bundle id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://roszkowski.dev/assets/images/ios-xcode-delete.mp4" rel="noopener noreferrer"&gt;mp4&lt;/a&gt;/&lt;a href="https://roszkowski.dev/assets/images/ios-xcode-delete.webm" rel="noopener noreferrer"&gt;webm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you should be able to have you &lt;code&gt;.ipa&lt;/code&gt; archive ready to submit to AppCenter, Beta or directly to your testers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjw8c0ecfzda5ksy5t6c1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjw8c0ecfzda5ksy5t6c1.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the &lt;a href="https://github.com/orestesgaolin/flutter-flavors/commit/a3c5512af215e48a9e38912c073ec4b79b41831b" rel="noopener noreferrer"&gt;commit a3c5512a&lt;/a&gt; to look through all the changes related to fastlane.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;After reading this article you should be able to configure Flutter flavors on your own. There are almost limitless possibilities related to flavors, schemes and configurations. For instance you can have separate Google Services files or Facebook ids for each flavor. You can enable or disable some features for test builds. You can even create multiple apps from single code base.&lt;/p&gt;

&lt;p&gt;I hope you learned something with me. See you soon in the next blog post 🖖.&lt;/p&gt;







&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Of course you can define as many flavors as you wish, 3 flavors are a good compromise ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;On Android you can't define &lt;em&gt;test&lt;/em&gt; flavor so we named it &lt;em&gt;tst&lt;/em&gt;, but we wanted &lt;em&gt;.test&lt;/em&gt; suffix to make it more obvious for QA. You can go with &lt;em&gt;test&lt;/em&gt; names and files all the way if you prefer it. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;This may be changed in future Flutter versions ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;I use several plugins to fastlane like &lt;a href="https://github.com/HazAT/fastlane-plugin-badge" rel="noopener noreferrer"&gt;badge&lt;/a&gt; or &lt;a href="https://github.com/microsoft/fastlane-plugin-appcenter" rel="noopener noreferrer"&gt;appcenter&lt;/a&gt;. I really recommend you to check them out. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>fastlane</category>
    </item>
  </channel>
</rss>
