<?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: Daniel Gomez</title>
    <description>The latest articles on DEV Community by Daniel Gomez (@danielgomezrico).</description>
    <link>https://dev.to/danielgomezrico</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%2F404483%2Fec8fbb3b-b2c0-4fca-95ac-4b11fc0d7ebb.jpeg</url>
      <title>DEV Community: Daniel Gomez</title>
      <link>https://dev.to/danielgomezrico</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danielgomezrico"/>
    <language>en</language>
    <item>
      <title>Building and Deploying Flutter Apps from CI with Fastlane: How to Reuse Code Efficiently</title>
      <dc:creator>Daniel Gomez</dc:creator>
      <pubDate>Thu, 27 May 2021 00:11:19 +0000</pubDate>
      <link>https://dev.to/danielgomezrico/how-to-build-flutter-apps-on-ci-with-fastlane-and-reuse-some-code-2np3</link>
      <guid>https://dev.to/danielgomezrico/how-to-build-flutter-apps-on-ci-with-fastlane-and-reuse-some-code-2np3</guid>
      <description>&lt;p&gt;We use &lt;code&gt;fastlane&lt;/code&gt; for our flutter apps, for ios and android builds, it give us a common language to define the setup and reuse code for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generating a changelog based on our changelog style&lt;/li&gt;
&lt;li&gt;Validate code style, formatting and lint &lt;/li&gt;
&lt;li&gt;Unit testing&lt;/li&gt;
&lt;li&gt;Verify that the project autogenerated code is been built fine.&lt;/li&gt;
&lt;li&gt;Verify that the project builds fine for each platform.&lt;/li&gt;
&lt;li&gt;Deploy to firebase app distribution for testing&lt;/li&gt;
&lt;li&gt;Deploying to each app store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are some tricks to be able to do it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a parent &lt;code&gt;fastfile&lt;/code&gt; where you have the reusable logic and one for each platform &lt;code&gt;ios/fastfile&lt;/code&gt; and &lt;code&gt;android/fastfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Have an easy way to run tasks from the root of the project, our idea was to create a lane &lt;code&gt;sh_on_root&lt;/code&gt; to run all flutter commands from each platform &lt;code&gt;fastfile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Gemfile&lt;/code&gt; for each platform and define &lt;code&gt;.ruby-version&lt;/code&gt; file to have a defined environment to run, helping us to avoid problems on CI server requiring some specific ruby version to run fastlane in there.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another thing we noticed is that using &lt;code&gt;--no-pub --suppress-analytics&lt;/code&gt; params from flutter commands saved us some time on CI if you want to try that's a good start.&lt;/p&gt;

&lt;p&gt;I will show an example of some usefull lanes and some workflows for them, you should think about your needs and call the lanes you need for each step but try to reuse for each platform some tasks having the parent &lt;code&gt;fastfile&lt;/code&gt; file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me the setup
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;Gemfile&lt;/code&gt; for each platform&lt;/li&gt;
&lt;li&gt;&lt;code&gt;android/Gemfile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ios/Gemfile&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="s2"&gt;"https://rubygems.org"&lt;/span&gt;

&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'cocoapods'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"fastlane"&lt;/span&gt;

&lt;span class="n"&gt;plugins_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s1"&gt;'fastlane'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Pluginfile'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;eval_gemfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugins_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plugins_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;.ruby-version&lt;/code&gt; so the CI (in our case Bitrise) have a clue of which ruby version should be used&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ios/.ruby-version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;android/.ruby-version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="mf"&gt;2.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create 3 fastlane files, 1 to be reused by each platform, and 1 for each platform (check that from each platform fastlane we import the parent one with &lt;code&gt;import "../../scripts/Fastfile"&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fastfile&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;opt_out_usage&lt;/span&gt;

&lt;span class="c1"&gt;# Have an easy way to get the root of the project&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;root_path&lt;/span&gt;
  &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*\Kfastlane/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*\Kandroid/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*\Kios/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.*\K\/\//&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Have an easy way to run flutter tasks on the root of the project&lt;/span&gt;
&lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:sh_on_root&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:command&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;sh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"cd &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Tasks to be reused on each platform flow&lt;/span&gt;
&lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:fetch_dependencies&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter pub get --suppress-analytics"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Tasks to be reused on each platform flow&lt;/span&gt;
&lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:build_autogenerated_code&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter pub run intl_utils:generate &amp;amp;&amp;amp; flutter pub run build_runner build --delete-conflicting-outputs"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Tasks to be reused on each platform flow&lt;/span&gt;
&lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:lint&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter format --suppress-analytics --set-exit-if-changed -n lib/main.dart lib/src/ test/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Tasks to be reused on each platform flow&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter test --no-pub --coverage --suppress-analytics"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ios/fastfile&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;"../../scripts/Fastfile"&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="c1"&gt;# Updates XCode project settings to use a different code signing based on method&lt;/span&gt;
  &lt;span class="n"&gt;private_lane&lt;/span&gt; &lt;span class="ss"&gt;:archive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:method&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;profile_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'ad-hoc'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"Distribution - Staging (adhoc)"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
    &lt;span class="n"&gt;update_code_signing_settings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;profile_name: &lt;/span&gt;&lt;span class="n"&gt;profile_name&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;export_method: &lt;/span&gt;&lt;span class="nb"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;private_lane&lt;/span&gt; &lt;span class="ss"&gt;:authenticate_apple_store&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;p8_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path_for_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"your .p8 file path"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;app_store_connect_api_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;key_id: &lt;/span&gt;&lt;span class="s2"&gt;"your key id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;issuer_id: &lt;/span&gt;&lt;span class="s2"&gt;"your used id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;key_filepath: &lt;/span&gt;&lt;span class="n"&gt;p8_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;duration: &lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# optional&lt;/span&gt;
      &lt;span class="ss"&gt;in_house: &lt;/span&gt;&lt;span class="kp"&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;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:build&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="n"&gt;fetch_dependencies&lt;/span&gt;
    &lt;span class="n"&gt;build_autogenerated_code&lt;/span&gt;


    &lt;span class="n"&gt;sign_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:sign_enabled&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;sign_param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sign_enabled&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'--no-codesign'&lt;/span&gt;

    &lt;span class="n"&gt;config_only&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:config_only&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kp"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;config_param&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config_only&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s1"&gt;'--config-only'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;

    &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter build ios --no-pub --suppress-analytics --release &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;sign_param&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;config_param&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:deploy_staging&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sign_enabled: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;archive&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="n"&gt;upload_symbols_to_crashlytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;dsym_path: &lt;/span&gt;&lt;span class="s2"&gt;"your.app.dSYM.zip"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;firebase_app_distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;app: &lt;/span&gt;&lt;span class="s2"&gt;"yours app id from firebase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;ipa_path: &lt;/span&gt;&lt;span class="s2"&gt;"your.ipa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;groups: &lt;/span&gt;&lt;span class="s2"&gt;"developers, staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;release_notes: &lt;/span&gt;&lt;span class="n"&gt;changelog&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:deploy_staging_testflight&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sign_enabled: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;archive&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;"app-store"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;authenticate_apple_store&lt;/span&gt;

    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="nb"&gt;test&lt;/span&gt;

    &lt;span class="n"&gt;upload_to_testflight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;ipa: &lt;/span&gt;&lt;span class="s2"&gt;"your.ipa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;reject_build_waiting_for_review: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_waiting_for_build_processing: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;distribute_external: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;notify_external_testers: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;groups: &lt;/span&gt;&lt;span class="s2"&gt;"Your testers"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:deploy_production&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;    
    &lt;span class="c1"&gt;# All certificates and .p8 file should be fine on runnning machine&lt;/span&gt;
    &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;sign_enabled: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;config_only: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;archive&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;"app-store"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;authenticate_apple_store&lt;/span&gt;

    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="nb"&gt;test&lt;/span&gt;

    &lt;span class="n"&gt;deliver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;ipa: &lt;/span&gt;&lt;span class="s2"&gt;"your.ipa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_metadata: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_screenshots: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;submit_for_review: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;force: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;automatic_release: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;submission_information: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;add_id_info_serves_ads: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;add_id_info_uses_idfa: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;export_compliance_uses_encryption: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="ss"&gt;precheck_include_in_app_purchases: &lt;/span&gt;&lt;span class="kp"&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;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;android/fastfile&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="s2"&gt;"../../scripts/Fastfile"&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;:android&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;:android&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;private_lane&lt;/span&gt; &lt;span class="ss"&gt;:build_apk&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="n"&gt;fetch_dependencies&lt;/span&gt;
    &lt;span class="n"&gt;build_autogenerated_code&lt;/span&gt;

    &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter build apk --release"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:build&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="n"&gt;fetch_dependencies&lt;/span&gt;
    &lt;span class="n"&gt;build_autogenerated_code&lt;/span&gt;

    &lt;span class="n"&gt;sh_on_root&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;command: &lt;/span&gt;&lt;span class="s2"&gt;"flutter build appbundle --no-pub --release --suppress-analytics"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:deploy_staging&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;build_apk&lt;/span&gt;

    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="nb"&gt;test&lt;/span&gt;

    &lt;span class="n"&gt;firebase_app_distribution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;app: &lt;/span&gt;&lt;span class="s2"&gt;"your app id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;groups: &lt;/span&gt;&lt;span class="s2"&gt;"your testers"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;android_artifact_type: &lt;/span&gt;&lt;span class="s2"&gt;"APK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;android_artifact_path: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;root_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/build/app/outputs/flutter-apk/app-release.apk"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;firebase_cli_path: &lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bin/firebase"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;lane&lt;/span&gt; &lt;span class="ss"&gt;:deploy_production&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;build&lt;/span&gt;

    &lt;span class="c1"&gt;# Reuse parent fastfile tasks&lt;/span&gt;
    &lt;span class="nb"&gt;test&lt;/span&gt;

    &lt;span class="n"&gt;supply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="ss"&gt;track: &lt;/span&gt;&lt;span class="s1"&gt;'beta'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;aab: &lt;/span&gt;&lt;span class="s2"&gt;"../build/app/outputs/bundle/release/app-release.aab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;json_key: &lt;/span&gt;&lt;span class="n"&gt;path_for_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"your play store.json"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;skip_upload_apk: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Upload the aab instead of apk&lt;/span&gt;
      &lt;span class="ss"&gt;skip_upload_metadata: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_upload_changelogs: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_upload_images: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;skip_upload_screenshots: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;)&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;



</description>
      <category>flutter</category>
      <category>ios</category>
      <category>fastlane</category>
      <category>android</category>
    </item>
    <item>
      <title>How to install an app on Android Work Profile for Debug</title>
      <dc:creator>Daniel Gomez</dc:creator>
      <pubDate>Thu, 11 Feb 2021 19:30:05 +0000</pubDate>
      <link>https://dev.to/danielgomezrico/how-to-install-an-app-on-android-work-profile-for-debug-4dn1</link>
      <guid>https://dev.to/danielgomezrico/how-to-install-an-app-on-android-work-profile-for-debug-4dn1</guid>
      <description>&lt;ol&gt;
&lt;li&gt;List all of your users (to find the number of the work profile (in the example the 11 is the work profile):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;adb shell pm list &lt;span class="nb"&gt;users
&lt;/span&gt;Users:
    UserInfo&lt;span class="o"&gt;{&lt;/span&gt;0:Propietario:13&lt;span class="o"&gt;}&lt;/span&gt; running
    UserInfo&lt;span class="o"&gt;{&lt;/span&gt;11:Perfil de trabajo:30&lt;span class="o"&gt;}&lt;/span&gt; running
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Copy the APK and then install it for that user with:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb push app-debug.apk /data/local/tmp 
adb shell pm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; 11 /data/local/tmp/app-debug.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>android</category>
      <category>workprofile</category>
      <category>debug</category>
      <category>adb</category>
    </item>
    <item>
      <title>Fastlane tips - How to setup IntelliJ for Fastlane and Fastfile</title>
      <dc:creator>Daniel Gomez</dc:creator>
      <pubDate>Wed, 09 Dec 2020 13:28:12 +0000</pubDate>
      <link>https://dev.to/danielgomezrico/how-to-setup-intellij-for-fastlane-and-fastfile-1cfe</link>
      <guid>https://dev.to/danielgomezrico/how-to-setup-intellij-for-fastlane-and-fastfile-1cfe</guid>
      <description>&lt;p&gt;If you use &lt;a href="https://fastlane.tools/" rel="noopener noreferrer"&gt;fastlane&lt;/a&gt; for your mobile environment and IntelliJ then you must be feeling the pain of opening your &lt;code&gt;fastlane/Fastfile&lt;/code&gt; in IntelliJ and see all without syntax highlights or autocomplete? &lt;/p&gt;

&lt;p&gt;That's because it does not have &lt;code&gt;.rb&lt;/code&gt; and so IntelliJ can't understand the language of the file, but there's one simple setup that will fix it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Settings / File Types&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for Ruby &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;code&gt;+&lt;/code&gt; and write &lt;code&gt;Fastfile&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example:&lt;br&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%2Fi%2F9awejq1ky86a6m706mv5.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%2Fi%2F9awejq1ky86a6m706mv5.png" alt="Intellij Setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://blog.jetbrains.com/idea/2010/09/custom-file-types-in-intellij-idea" rel="noopener noreferrer"&gt;custom-file-types-in-intellij-idea&lt;/a&gt;&lt;/p&gt;

</description>
      <category>fastlane</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Test Code Sugaring - Using types and code extensions to improve code expressiveness</title>
      <dc:creator>Daniel Gomez</dc:creator>
      <pubDate>Sat, 15 Aug 2020 22:07:58 +0000</pubDate>
      <link>https://dev.to/danielgomezrico/using-types-to-improve-separation-of-concerns-and-unit-test-easily-on-flutter-any-typed-language-fim</link>
      <guid>https://dev.to/danielgomezrico/using-types-to-improve-separation-of-concerns-and-unit-test-easily-on-flutter-any-typed-language-fim</guid>
      <description>&lt;p&gt;All of this is applicable of any typed language, I used it in android with Kotlin in Android and now Im happy to use them in Flutter also (the at the bottom of the post to see an example of how you can do it on android and on dart).&lt;/p&gt;

&lt;h1&gt;
  
  
  Without types (be sad)
&lt;/h1&gt;

&lt;p&gt;Lets assume that you are testing a presenter 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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginPresenter&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;userRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onLogin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&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="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;httpCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Parse the response body and do something with it&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="c1"&gt;// parse the response body to get the errors&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;That is using some abstractions like this ones:&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;Response&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;body&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;httpCode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
      &lt;span class="c1"&gt;// Do the Http call here and return the http response&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;h2&gt;
  
  
  How your tests may look
&lt;/h2&gt;

&lt;p&gt;Using mockito to create the mocks:&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'on success'&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="n"&gt;whenever&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;thenAnswer&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your big json response"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
       &lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onLogin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

       &lt;span class="c1"&gt;// expect each thing based on the json response values&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;h2&gt;
  
  
  Problems with this
&lt;/h2&gt;

&lt;p&gt;The problems with this kind of tests are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should be testing only 1 thing in a unit test, with this, you are testing the JSON parsed object, think the case that the service you are using changed the representation of the response to something else, you would have to go and change every BIG json string file and pray :P.&lt;/li&gt;
&lt;li&gt;There are no so much separation of concerns since the presenter know what you are using Http to fetch the data, also know that 200 is a success, what if the service starts answering with 201? &lt;/li&gt;
&lt;li&gt;You will end up with big Json strings representing each possible state that the presenter will handle and that your service may answer. &lt;em&gt;You will suffer maintaining this JSONs over the time&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If you have to mock multiple repositories it is a little bit fill of boilerplate like returning a &lt;code&gt;Future.value&lt;/code&gt; that is inside a function &lt;code&gt;() =&amp;gt; ...&lt;/code&gt; and the 200 again everywhere.&lt;/li&gt;
&lt;li&gt;The presenters should parse the JSON file to get the errors and handle each case, and sometimes it requires you to use the http code together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this is a lot of irresponsibility for a presenter doesn't it? when something does a lot of things then testing and maintaining those tests is &lt;del&gt;a hell&lt;/del&gt; &lt;em&gt;really hard&lt;/em&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Adding types for the rescue!
&lt;/h1&gt;

&lt;p&gt;You can add some types to your system to represent the Success and the Failure of any task everywhere.&lt;/p&gt;

&lt;p&gt;You can add some simple abstractions:&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;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;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;T&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="o"&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;body&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;cause&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;Failure&lt;/span&gt;&lt;span class="o"&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;cause&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;I&lt;/p&gt;

&lt;p&gt;And a repository that returns responses with &lt;code&gt;Result&lt;/code&gt; class, like:&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;Session&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;userId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
      &lt;span class="c1"&gt;// Do the Http call here and get the user id...&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 the presenters will look a cleaner:&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;LoginPresenter&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;userRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UserRepository&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;onLogin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;async&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&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="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="c1"&gt;// use result.body.userId been sure that it is an int&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="c1"&gt;// use result.message been sure that theres a string with the error&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;h2&gt;
  
  
  Goods of this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You remove a lot of knowledge from presenters, they will not know anything about Http, http codes or json parsing.&lt;/li&gt;
&lt;li&gt;You defined types for responses an so presenters can be sure that the response is a &lt;code&gt;Session&lt;/code&gt; that have an int attribute with the userId, so no double check or casting from string to the type you are supposed to get.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  How tests benefits from this
&lt;/h1&gt;

&lt;p&gt;Now that you know that your repositories responses your types, you can add extensions 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;extension&lt;/span&gt; &lt;span class="n"&gt;ResultMock&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;PostExpectation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// One for success&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;thenSucceed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;thenAnswer&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;realInvocation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// One for failures&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;thenFail&lt;/span&gt;&lt;span class="o"&gt;([&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'mock-error'&lt;/span&gt;&lt;span class="o"&gt;])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;thenAnswer&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;realInvocation&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;Then the tests may look like:&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'on success'&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="n"&gt;whenever&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;thenSucceed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Session&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="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onLogin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

       &lt;span class="c1"&gt;// expect each thing based on the json response values&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;h2&gt;
  
  
  What you get from this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Tests are testing only one thing and that's the behaviour of the presenter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No need to maintain JSONS everywhere, you can add tests in the repository to test the parsing if that's what you want to, but they will be in only one place of your system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You will mock easily everywhere since you don't need to think that you need to create a Future, a lambda that answers that future and the proper result type, just use the extension with the response type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some people prefer to use fake implementations of their abstractions to "mock responses", that is not the topic of this post to' I believe that with this you may not need to create fake implementations, mocking became so easy with this...&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This can be used on any typed language, here are some examples of &lt;code&gt;dart&lt;/code&gt; and &lt;code&gt;kotlin&lt;/code&gt; extensions if you come from android and want to try it:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


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



&lt;p&gt;Types are a really powerful thing to separate concerns of your system.&lt;/p&gt;

</description>
      <category>dart</category>
      <category>testing</category>
      <category>architecture</category>
      <category>flutter</category>
    </item>
    <item>
      <title>MaterialComponents, CoordinatorLayout, AppBarLayout, Toolbar and automatic elevation Spike
</title>
      <dc:creator>Daniel Gomez</dc:creator>
      <pubDate>Tue, 09 Jun 2020 11:54:36 +0000</pubDate>
      <link>https://dev.to/danielgomezrico/materialcomponents-coordinatorlayout-appbarlayout-toolbar-and-automatic-elevation-spike-oo0</link>
      <guid>https://dev.to/danielgomezrico/materialcomponents-coordinatorlayout-appbarlayout-toolbar-and-automatic-elevation-spike-oo0</guid>
      <description>&lt;h1&gt;
  
  
  MaterialComponents, CoordinatorLayout, AppBarLayout, Toolbar and automatic elevation Spike
&lt;/h1&gt;

&lt;p&gt;Here's an example of how to use themes to get automatic &lt;code&gt;Toolbar&lt;/code&gt; elevation without extra code or custom listeners.&lt;/p&gt;

&lt;p&gt;Use &lt;a href="https://developer.android.com/reference/com/google/android/material/appbar/AppBarLayout"&gt;AppBarLayout&lt;/a&gt;&lt;br&gt;
from &lt;a href="https://github.com/material-components/material-components-android"&gt;MaterialComponents&lt;/a&gt; for it.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Your app style should use a &lt;code&gt;MaterialCompoment&lt;/code&gt; style, like &lt;a href="//src/main/res/values/styles.xml"&gt;src/main/res/values/styles.xml&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup you AppBarLayout:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use any MaterialCompoments style for this component like: &lt;code&gt;Widget.MaterialComponents.AppBarLayout.Surface&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;app:liftOnScroll="true"&lt;/code&gt; to enable the automatic elevation based on scroll.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup your scrolling view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;app:layout_behavior="@string/appbar_scrolling_view_behavior&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's all :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Results
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yXEhznnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7frjikldbx3qhuvy8sb6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yXEhznnt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/7frjikldbx3qhuvy8sb6.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
    </item>
  </channel>
</rss>
