<?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: Wesley Oliveira</title>
    <description>The latest articles on DEV Community by Wesley Oliveira (@wesleybruno).</description>
    <link>https://dev.to/wesleybruno</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F557835%2F6a3a6a04-7b53-4ddd-928c-c6c7b3751f7f.jpeg</url>
      <title>DEV Community: Wesley Oliveira</title>
      <link>https://dev.to/wesleybruno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wesleybruno"/>
    <language>en</language>
    <item>
      <title>White-Label em Flutter: duas filosofias, três técnicas</title>
      <dc:creator>Wesley Oliveira</dc:creator>
      <pubDate>Mon, 28 Sep 2026 14:06:55 +0000</pubDate>
      <link>https://dev.to/wesleybruno/white-label-em-flutter-duas-filosofias-tres-tecnicas-1o2b</link>
      <guid>https://dev.to/wesleybruno/white-label-em-flutter-duas-filosofias-tres-tecnicas-1o2b</guid>
      <description>&lt;p&gt;Se você já precisou publicar o "mesmo app" para 5, 10 ou 20 marcas diferentes, já bateu de frente com a pergunta: &lt;strong&gt;onde mora a diferença entre um tenant e outro?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ &lt;strong&gt;Flavors&lt;/strong&gt;: build por tenant, isolamento total (applicationId, ícone), CI cresce com cada marca.&lt;/li&gt;
&lt;li&gt;📁 &lt;strong&gt;Pasta + script&lt;/strong&gt;: mais simples que flavors, boa quando a diferença é só tema/assets.&lt;/li&gt;
&lt;li&gt;🌐 &lt;strong&gt;Config por endpoint&lt;/strong&gt;: 1 build para todos, muda sem rebuild, mas depende de rede e de isolamento no backend.&lt;/li&gt;
&lt;li&gt;Na prática, a maioria combina as três — veja o guia de decisão no fim.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existem duas filosofias possíveis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build-time&lt;/strong&gt;: cada tenant vira um build separado. A diferença é resolvida antes do app existir, em tempo de compilação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt;: existe um único build. A diferença é resolvida depois que o app já está rodando, buscando configuração de fora.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dentro da filosofia &lt;em&gt;build-time&lt;/em&gt; cabem duas técnicas bem diferentes na prática — &lt;strong&gt;flavors&lt;/strong&gt; e &lt;strong&gt;pasta por tenant com script de troca&lt;/strong&gt;. E a filosofia &lt;em&gt;runtime&lt;/em&gt; geralmente se resolve com &lt;strong&gt;configuração buscada de um endpoint&lt;/strong&gt;. Este artigo cobre as três, com código, trade-offs reais e um guia de quando usar cada uma.&lt;/p&gt;

&lt;p&gt;Não existe "a certa". Existe a certa para o seu número de tenants, sua frequência de mudança de marca e sua tolerância a builds manuais.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visão geral rápida
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Flavors&lt;/th&gt;
&lt;th&gt;Pasta por tenant + script&lt;/th&gt;
&lt;th&gt;Endpoint de config&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Filosofia&lt;/td&gt;
&lt;td&gt;Build-time&lt;/td&gt;
&lt;td&gt;Build-time&lt;/td&gt;
&lt;td&gt;Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quantos builds/artefatos&lt;/td&gt;
&lt;td&gt;1 por tenant&lt;/td&gt;
&lt;td&gt;1 por tenant&lt;/td&gt;
&lt;td&gt;1 (todos os tenants)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precisa rebuild pra mudar cor/logo&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Funciona offline&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;td&gt;Só com cache local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Novo tenant sem tocar em código&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;td&gt;Parcialmente&lt;/td&gt;
&lt;td&gt;Sim, se só muda tema/feature flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risco de configuração vazar entre tenants&lt;/td&gt;
&lt;td&gt;Baixo (isolado no bundle)&lt;/td&gt;
&lt;td&gt;Baixo (isolado no bundle)&lt;/td&gt;
&lt;td&gt;Precisa de cuidado no backend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Esforço de CI/CD&lt;/td&gt;
&lt;td&gt;Alto (N pipelines)&lt;/td&gt;
&lt;td&gt;Médio (1 pipeline + parâmetro)&lt;/td&gt;
&lt;td&gt;Baixo (1 pipeline)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bom para&lt;/td&gt;
&lt;td&gt;Poucos tenants, diferenças profundas (ícone, nome do app, permissões nativas)&lt;/td&gt;
&lt;td&gt;Poucos a médios tenants, times pequenos, sem infra de backend dedicada&lt;/td&gt;
&lt;td&gt;Muitos tenants, mudanças de marca frequentes, mesma regra de negócio&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Na prática, a maioria dos projetos maduros acaba &lt;strong&gt;combinando build-time para o que é fixo&lt;/strong&gt; (application ID, nome do app, ícone, permissões nativas) &lt;strong&gt;com runtime para o que muda com frequência&lt;/strong&gt; (cores, textos, feature flags). Mas vamos por partes.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Build-time: Flavors
&lt;/h2&gt;

&lt;p&gt;Flavors (ou &lt;em&gt;product flavors&lt;/em&gt;, no Android; &lt;em&gt;schemes&lt;/em&gt; + &lt;em&gt;configurations&lt;/em&gt;, no iOS) fazem o Flutter gerar builds distintos a partir da mesma base de código, cada um com seu próprio &lt;code&gt;applicationId&lt;/code&gt;/&lt;code&gt;bundle identifier&lt;/code&gt;, ícone, nome e ponto de entrada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estrutura
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
  main_tenant_a.dart
  main_tenant_b.dart
  main_common.dart        # bootstrap compartilhado
android/
  app/
    build.gradle           # flavorDimensions + productFlavors
ios/
  Runner.xcodeproj         # schemes + xcconfig por tenant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Android — &lt;code&gt;android/app/build.gradle&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;flavorDimensions&lt;/span&gt; &lt;span class="s2"&gt;"tenant"&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;tenantA&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;"tenant"&lt;/span&gt;
            &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.example.tenanta"&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Tenant A"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;tenantB&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;"tenant"&lt;/span&gt;
            &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.example.tenantb"&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Tenant B"&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;h3&gt;
  
  
  iOS
&lt;/h3&gt;

&lt;p&gt;No iOS não existe "flavor" nativo — a prática comum é criar uma &lt;strong&gt;Configuration&lt;/strong&gt; (Debug-TenantA, Release-TenantA, etc.) e um &lt;strong&gt;Scheme&lt;/strong&gt; por tenant, cada um apontando para um &lt;code&gt;.xcconfig&lt;/code&gt; próprio com &lt;code&gt;PRODUCT_BUNDLE_IDENTIFIER&lt;/code&gt;, &lt;code&gt;PRODUCT_NAME&lt;/code&gt; e o &lt;code&gt;AppIcon&lt;/code&gt; correspondente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entry point compartilhado
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/main_common.dart&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;bootstrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantConfig&lt;/span&gt; &lt;span class="n"&gt;config&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="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="nl"&gt;config:&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;span class="c1"&gt;// lib/main_tenant_a.dart&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tenantA&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// lib/main_tenant_b.dart&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tenantB&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rodando e buildando
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter run &lt;span class="nt"&gt;--flavor&lt;/span&gt; tenantA &lt;span class="nt"&gt;-t&lt;/span&gt; lib/main_tenant_a.dart
flutter build apk &lt;span class="nt"&gt;--flavor&lt;/span&gt; tenantA &lt;span class="nt"&gt;-t&lt;/span&gt; lib/main_tenant_a.dart
flutter build ipa &lt;span class="nt"&gt;--flavor&lt;/span&gt; tenantA &lt;span class="nt"&gt;-t&lt;/span&gt; lib/main_tenant_a.dart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A favor:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Isolamento real. &lt;code&gt;applicationId&lt;/code&gt;, ícone, nome e permissões nativas (&lt;code&gt;AndroidManifest.xml&lt;/code&gt;, &lt;code&gt;Info.plist&lt;/code&gt;) podem divergir de verdade entre tenants — coisa que uma configuração remota não alcança.&lt;/li&gt;
&lt;li&gt;Cada tenant é um artefato de loja independente, o que é exigido quando cada marca precisa aparecer com seu próprio nome na App Store/Play Store.&lt;/li&gt;
&lt;li&gt;Funciona 100% offline, porque tudo já está compilado no binário.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contra:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI/CD cresce linearmente com o número de tenants. Com 15 tenants, são 15 pipelines de build (ou 1 pipeline com matriz de 15 combinações).&lt;/li&gt;
&lt;li&gt;Mudar uma cor ou um texto exige rebuild, novo upload e nova aprovação de loja.&lt;/li&gt;
&lt;li&gt;Acima de ~10-15 tenants o &lt;code&gt;build.gradle&lt;/code&gt; e os schemes do Xcode viram um arquivo difícil de manter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use quando:&lt;/strong&gt; o número de tenants é baixo (tipicamente até 10-15), as diferenças vão além de tema (ícone, nome do app, application ID, permissões nativas) e builds separados por marca são um requisito de negócio, não um detalhe técnico.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Build-time: pasta por tenant + script de troca
&lt;/h2&gt;

&lt;p&gt;Uma variação mais simples de manter que flavors quando a diferença entre tenants é &lt;strong&gt;majoritariamente assets e configuração&lt;/strong&gt;, não &lt;code&gt;applicationId&lt;/code&gt; nem permissões nativas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estrutura
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tenants/
  tenant_a/
    assets/
      logo.png
      splash.png
    theme.json
  tenant_b/
    assets/
      logo.png
      splash.png
    theme.json
assets/
  active/            # gerado pelo script, git-ignored
    logo.png
    splash.png
    theme.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Script de troca
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# scripts/select_tenant.sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;TENANT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:?Uso:&lt;span class="p"&gt; select_tenant.sh &amp;lt;nome-do-tenant&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;SRC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"tenants/&lt;/span&gt;&lt;span class="nv"&gt;$TENANT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"assets/active"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tenant '&lt;/span&gt;&lt;span class="nv"&gt;$TENANT&lt;/span&gt;&lt;span class="s2"&gt;' não encontrado em tenants/"&lt;/span&gt;
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC&lt;/span&gt;&lt;span class="s2"&gt;/assets/."&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SRC&lt;/span&gt;&lt;span class="s2"&gt;/theme.json"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEST&lt;/span&gt;&lt;span class="s2"&gt;/theme.json"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Tenant ativo: &lt;/span&gt;&lt;span class="nv"&gt;$TENANT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x scripts/select_tenant.sh
./scripts/select_tenant.sh tenant_a
flutter build apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;pubspec.yaml&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;assets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;assets/active/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lendo o tema em runtime (mas ainda dentro do bundle)
&lt;/h3&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;ThemeLoader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&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="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;load&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;raw&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;rootBundle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'assets/active/theme.json'&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;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;primaryColor:&lt;/span&gt; &lt;span class="n"&gt;Color&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="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'primaryColor'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="c1"&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;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A favor:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Muito mais simples de configurar que flavors — sem tocar em Gradle nem em Xcode.&lt;/li&gt;
&lt;li&gt;Um único pipeline de CI, parametrizado pelo nome do tenant.&lt;/li&gt;
&lt;li&gt;Onboarding de tenant novo é criar uma pasta e preencher &lt;code&gt;theme.json&lt;/code&gt;, sem escrever código.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contra:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ainda gera um binário por tenant — não resolve o problema de "1 app, N marcas" em runtime.&lt;/li&gt;
&lt;li&gt;Se &lt;code&gt;applicationId&lt;/code&gt; e permissões nativas também precisam mudar por tenant, o script sozinho não cobre; você acaba combinando com flavors de qualquer forma.&lt;/li&gt;
&lt;li&gt;Fácil esquecer de rodar o script antes do build errado e publicar o tenant errado — vale um passo de CI que falha se &lt;code&gt;assets/active&lt;/code&gt; estiver vazio ou não bater com o parâmetro do build.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use quando:&lt;/strong&gt; os tenants diferem principalmente em tema, logo e textos, o time é pequeno, e configurar Gradle flavors + Xcode schemes para cada marca nova é mais trabalho do que o problema justifica.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Runtime: configuração por endpoint
&lt;/h2&gt;

&lt;p&gt;Aqui a filosofia muda: existe &lt;strong&gt;um único build&lt;/strong&gt;, publicado uma vez, e o tenant é resolvido depois que o app já está instalado no dispositivo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Como o app descobre "quem ele é"
&lt;/h3&gt;

&lt;p&gt;Algumas estratégias comuns, sozinhas ou combinadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domínio de login&lt;/strong&gt;: o usuário digita &lt;code&gt;empresa.suaplataforma.com&lt;/code&gt; ou seleciona a empresa numa tela inicial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep link / QR code de convite&lt;/strong&gt;: o link de instalação já carrega o &lt;code&gt;tenant_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login primeiro, tenant depois&lt;/strong&gt;: o backend de autenticação retorna o &lt;code&gt;tenant_id&lt;/code&gt; junto do token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Buscando e aplicando a configuração
&lt;/h3&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;TenantConfig&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;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&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;Color&lt;/span&gt; &lt;span class="n"&gt;primaryColor&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;String&lt;/span&gt; &lt;span class="n"&gt;logoUrl&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;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&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;tenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&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;primaryColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&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;logoUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&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;features&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&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;TenantConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;tenantId:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'tenantId'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;primaryColor:&lt;/span&gt; &lt;span class="n"&gt;Color&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="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'primaryColor'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
      &lt;span class="nl"&gt;logoUrl:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'logoUrl'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;features:&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'features'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&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;class&lt;/span&gt; &lt;span class="nc"&gt;TenantRepository&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;Dio&lt;/span&gt; &lt;span class="n"&gt;_dio&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;Box&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Hive, por exemplo&lt;/span&gt;

  &lt;span class="n"&gt;TenantRepository&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;_dio&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;_cache&lt;/span&gt;&lt;span class="p"&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="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&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="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;response&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;_dio&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/tenants/&lt;/span&gt;&lt;span class="si"&gt;$tenantId&lt;/span&gt;&lt;span class="s"&gt;/config'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;5&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&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;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tenant_config_&lt;/span&gt;&lt;span class="si"&gt;$tenantId&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&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;data&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;config&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;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Sem rede ou timeout: cai para o último config salvo.&lt;/span&gt;
      &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tenant_config_&lt;/span&gt;&lt;span class="si"&gt;$tenantId&lt;/span&gt;&lt;span class="s"&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;cached&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="c1"&gt;// Sem cache e sem rede: fallback para um tema neutro,&lt;/span&gt;
      &lt;span class="c1"&gt;// nunca para o tema de outro tenant.&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;TenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Bootstrap&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="n"&gt;WidgetsFlutterBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ensureInitialized&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;tenantId&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;TenantResolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// domínio, deep link, etc.&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;TenantRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cacheBox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;buildTheme&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="nl"&gt;features:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature flags junto do tema
&lt;/h3&gt;

&lt;p&gt;Já que a config vem de um endpoint, é natural embutir &lt;em&gt;feature flags&lt;/em&gt; no mesmo payload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tenant_a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"primaryColor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xFF2DD4BF"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logoUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.exemplo.com/tenant_a/logo.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&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;"pixEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cardVirtualEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"biometricLogin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'pixEnabled'&lt;/span&gt;&lt;span class="p"&gt;]&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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;PixButton&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;A favor:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um único build, uma única submissão de loja (quando o nome do app e o ícone não variam por marca).&lt;/li&gt;
&lt;li&gt;Mudar cor, texto ou ligar/desligar uma feature não exige rebuild nem nova aprovação de loja — é uma alteração no backend.&lt;/li&gt;
&lt;li&gt;Escala bem para dezenas de tenants sem custo de CI/CD adicional por tenant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contra:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Depende de rede na primeira execução (ou de um cache/fallback bem pensado para funcionar offline depois).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;applicationId&lt;/code&gt;, nome do app e ícone na loja continuam sendo os mesmos para todo mundo — se cada marca precisa de um app com nome próprio na loja, essa técnica sozinha não resolve.&lt;/li&gt;
&lt;li&gt;Superfície de erro nova: um bug no backend de config pode quebrar o tema de todos os tenants ao mesmo tempo, ou pior, vazar a configuração de um tenant para outro se o endpoint não isolar bem por &lt;code&gt;tenant_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Exige uma peça de infraestrutura (o serviço de config) que as duas técnicas anteriores não exigem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use quando:&lt;/strong&gt; o número de tenants é alto, a marca muda com frequência (rebranding, campanhas sazonais, testes A/B de tema), e o &lt;code&gt;applicationId&lt;/code&gt;/nome na loja pode ser compartilhado entre tenants (por exemplo, um único app "guarda-chuva" com seleção de empresa no login).&lt;/p&gt;




&lt;h2&gt;
  
  
  Combinando as três (o caminho mais comum na prática)
&lt;/h2&gt;

&lt;p&gt;Projetos com white label maduro raramente escolhem só uma técnica. Um padrão comum:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flavors&lt;/strong&gt; resolvem o que é impossível de mudar em runtime: &lt;code&gt;applicationId&lt;/code&gt;, nome do app na loja, ícone, permissões nativas — geralmente para um número pequeno de tenants "grandes" que exigem app próprio na loja.&lt;/li&gt;
&lt;li&gt;Dentro de cada flavor, &lt;strong&gt;configuração por endpoint&lt;/strong&gt; resolve tema, textos e feature flags — porque mesmo o tenant que tem app próprio na loja quer poder trocar a cor do botão sem esperar aprovação da Apple.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pasta por tenant + script&lt;/strong&gt; aparece como uma alternativa mais leve a flavors quando o requisito de &lt;code&gt;applicationId&lt;/code&gt; próprio não existe, mas ainda se quer builds separados (por exemplo, para times menores que não querem manter uma infra de config remota).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ou seja: build-time para o que é estrutural e raramente muda, runtime para o que é visual/comportamental e muda o tempo todo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Guia de decisão rápido
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cada marca precisa aparecer com nome e ícone próprios na loja?&lt;/strong&gt; → precisa de flavors (ou pasta + script), não tem como fugir disso só com endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Menos de ~10 tenants, sem infraestrutura de backend dedicada, diferenças são só visuais?&lt;/strong&gt; → pasta por tenant + script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mais de ~15-20 tenants, marca muda com frequência, mesmo &lt;code&gt;applicationId&lt;/code&gt; para todos é aceitável?&lt;/strong&gt; → configuração por endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tudo isso ao mesmo tempo?&lt;/strong&gt; → flavors (ou pasta) para o estrutural, endpoint para o resto. É mais trabalho de arquitetura, mas é o que escala.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Fechando
&lt;/h2&gt;

&lt;p&gt;Nenhuma das três técnicas é "melhor" isoladamente — cada uma resolve um tipo de variação diferente entre tenants. O erro mais comum é tentar forçar o endpoint de config a fazer o trabalho dos flavors (mudar &lt;code&gt;applicationId&lt;/code&gt; em runtime é impossível) ou o contrário, usar flavors para algo que muda toda semana e devia estar num backend.&lt;/p&gt;

&lt;p&gt;Se você já implementou white label em Flutter, queria saber: qual dessas técnicas você usou, e o que te fez trocar de uma para outra?&lt;/p&gt;




&lt;p&gt;Trabalho como Flutter Sênior remoto, com Go como segunda stack. Se quiser trocar ideia sobre isso ou sobre outras arquiteturas de white label, me encontra no &lt;a href="https://github.com/wesleybruno" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; ou no &lt;a href="https://www.linkedin.com/in/wesley-oliveira-/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>dart</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Flutter - Builder Pattern - Gerando textos estilizados</title>
      <dc:creator>Wesley Oliveira</dc:creator>
      <pubDate>Thu, 11 Feb 2021 15:10:52 +0000</pubDate>
      <link>https://dev.to/wesleybruno/flutter-builder-pattern-gerando-textos-estilizados-5bkl</link>
      <guid>https://dev.to/wesleybruno/flutter-builder-pattern-gerando-textos-estilizados-5bkl</guid>
      <description>&lt;p&gt;Recentemente comecei a estudar um pouco de Swift/IOS para poder &lt;em&gt;"sair do 0"&lt;/em&gt;, saber como funciona as coisas nesse mundo até então desconhecido para mim.&lt;/p&gt;

&lt;p&gt;Resolvi fazer um curso básico no site, &lt;a href="https://www.raywenderlich.com" rel="noopener noreferrer"&gt;Raywenderlich&lt;/a&gt; , que por sinal um excelente curso.&lt;/p&gt;

&lt;p&gt;Logo no começo do curso achei diferente a forma como são gerados os componentes visuais, diferente das widgets do flutter lá tudo são views.&lt;/p&gt;

&lt;p&gt;A forma como são geradas essas views lembram bastante o design pattern builder ¹, como pode ser visto abaixo.&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%2Fi%2F3utwyoplacblc7mbo2y8.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%2Fi%2F3utwyoplacblc7mbo2y8.PNG" alt="IOS Text View" width="229" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;¹ Caso não tenha conhecimento sobre esse padrão sugiro assistir esse vídeo &lt;a href="https://www.youtube.com/watch?v=x5u9VOdnZ3Q&amp;amp;t=764s" rel="noopener noreferrer"&gt;Builder Pattern&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Transformando a view acima para flutter teriamos algo semelhante a isto:&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%2Fi%2Fh9p3j9roiili6czyckup.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%2Fi%2Fh9p3j9roiili6czyckup.png" alt="Flutter Text Widget" width="381" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como pode ser visto tem uma grande diferença na forma como são criados.&lt;/p&gt;

&lt;p&gt;A partir desse ponto, irei focar apenas no flutter.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;Imagine uma situação onde em uma mesma linha deva ter diversos estilos textuais aplicados, por exemplo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Texto em negrito&lt;/strong&gt;, &lt;em&gt;texto em italico&lt;/em&gt;, texto normal.&lt;/p&gt;

&lt;p&gt;Flutter tem o poder de transformar isso em tarefa bem simples, aplicando apenas uma Text com alguns TextSpan isso se torna uma tarefa fácil como está na imagem abaixo.&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%2Fi%2F7gqjb98ocb7m86civs8n.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%2Fi%2F7gqjb98ocb7m86civs8n.png" alt="Flutter text widget 2" width="410" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E se forem mais estilos, 10, 20, claro que isso é um cenário difícil de acontecer, mas caso aconteça, o quanto esse widget iria crescer?&lt;/p&gt;

&lt;h2&gt;
  
  
  Solução proposta
&lt;/h2&gt;

&lt;p&gt;Pensando nesse improvável panorama, lembrei da implementação do IOS e com o pattern Builder, pensei em tentar adaptar para o flutter.&lt;/p&gt;

&lt;p&gt;Depois de algum tempo estudando, cheguei a esse pequeno esboço.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wesleybruno/flutter_gerador_textos_estilizados" rel="noopener noreferrer"&gt;Flutter Gerador Textos Estilizados&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A implementação foi feita seguindo o Builder pattern, e para utiliza-lo ficou bastante simples.&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%2Fi%2Fs2vgc82qf2q9h9l1lb2c.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%2Fi%2Fs2vgc82qf2q9h9l1lb2c.png" alt="Gerador Textos Widget" width="421" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como tudo no mundo da programação, não existe uma bala de prata! Existem N cenários e cada um deve ser avaliado a melhor opção, além de que isso é apenas uma ideia de algo pode ou não ser útil para alguém, mas afim de compartilhar um aprendizado resolvi escrever esse post.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>ios</category>
      <category>mobile</category>
      <category>swift</category>
    </item>
    <item>
      <title>Flutter Image File Upload - Base64 x MultiPart Form</title>
      <dc:creator>Wesley Oliveira</dc:creator>
      <pubDate>Mon, 11 Jan 2021 14:34:34 +0000</pubDate>
      <link>https://dev.to/wesleybruno/flutter-file-upload-base64-x-multipart-form-2a2d</link>
      <guid>https://dev.to/wesleybruno/flutter-file-upload-base64-x-multipart-form-2a2d</guid>
      <description>&lt;p&gt;Recentemente comecei a pesquisar diferentes temas que poderiam ser utilizados para escrever algum conteúdo, foi aí que um amigo escreveu o primeiro artigo e utilizou de um problema que aconteceu em seu dia a dia para transformá-lo em uma ideia de artigo, para escrever esse pequeno artigo resolvi tentar da mesma maneira.&lt;/p&gt;

&lt;p&gt;Passamos por uns erros no quesito de upload de uma imagem para a api via Base64, resolvi então mostrar aqui alguns problemas dessa abordagem e uma alternativa para a mesma.&lt;/p&gt;

&lt;p&gt;Base64 é um método para codificação de dados para transferência na Internet. É utilizado frequentemente para transmitir dados binários por meios de transmissão que lidam apenas com texto, como por exemplo para enviar arquivos anexos por e-mail.&lt;/p&gt;

&lt;p&gt;Para converter um arquivo para Base64 no flutter é muito simples vide código baixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ImagePicker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pickImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="nx"&gt;base64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;base64Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readAsBytesSync&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;¹ ImagePicker - Plugin Flutter para iOS e Android para selecionar imagens da galeria ou tirar uma nova foto com a câmera. &lt;a href="https://pub.dev/packages/image_picker" rel="noopener noreferrer"&gt;Link&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Obtido o Base64, o mesmo pode ser enviado no &lt;em&gt;body&lt;/em&gt; de uma requisição &lt;em&gt;post&lt;/em&gt; por exemplo.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problemas com Base64
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Respostas de API ficam muito grandes
&lt;/h3&gt;

&lt;p&gt;Respostas muito grandes tendem a levar mais tempo para serem processadas dando mais tempo para uma possível falha na rede e aumentando o consumo de dados do dispositivo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Oversizing
&lt;/h3&gt;

&lt;p&gt;O tamanho da imagem codificada em Base64 é aproximadamente 30% maior do que a imagem binária original.&lt;/p&gt;

&lt;h3&gt;
  
  
  A experiência do usuário é afetada negativamente.
&lt;/h3&gt;

&lt;p&gt;Incluindo os retornos junto com outras respostas da API, o aplicativo irá esperar carregar todos os dados antes de exibir informações na tela, afetando assim a experiência do usuário que verá uma tela de carregamento por mais tempo.&lt;/p&gt;

&lt;h3&gt;
  
  
  O armazenamento em cache de imagens no dispositivo não é mais possível.
&lt;/h3&gt;

&lt;p&gt;Imagens enviadas para o dispositivo via Base64 tornará irrelevante qualquer implementação de serviços de cache de armazenamento de imagens.&lt;/p&gt;

&lt;p&gt;Recursos nativos do flutter como o precacheImage tornam-se inutilizáveis nesta situação, já que toda vez será necessário fazer o decode do base64.&lt;/p&gt;

&lt;p&gt;Outra implementação que se torna inutilizável seria o cachedNetworkImage, outra implementação nativa do flutter, mas que não pode ser  aplicada ao contexto de um Base64.&lt;/p&gt;

&lt;p&gt;Para aplicativos que possam funcionar tanto online quanto offline, a utilização de cache de imagens é um recurso bem eficaz para melhor experiência do usuário.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vantagens do Base64
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Remover a necessidade de múltiplos request, para obtenção de dados. ²&lt;/li&gt;
&lt;li&gt;Imagens codificadas como Base64 podem ser armazenadas em bancos de dados e gerados novos arquivos.
² Como foi mostrado antes, essa abordagem pode ser uma vantagem ou uma desvantagem a depender do contexto e da implementação&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Multipart Form Data
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;É uma de três formas de codificação existentes no HTML sendo elas: &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;1.application/x-www-form-urlencoded &lt;br&gt;
2.multipart/form-data&lt;br&gt;
3.text/plain&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deve-se utilizar esta forma de codificação quando a requisição possuir algum arquivo que deve fazer upload &lt;/li&gt;
&lt;li&gt;É compatível com todos os navegadores.&lt;/li&gt;
&lt;li&gt;Suporta arquivos com tamanhos muito grandes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Utilizando multipart com Flutter
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;File&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ImagePicker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pickImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ImageSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;final&lt;/span&gt; &lt;span class="nx"&gt;multiPartdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;MultipartFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;final&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromMap&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;multiPartdata&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;Desta maneira o servidor da API deve estar preparado para receber requisições com o cabeçalho multipart/form-data, já que o conteúdo não irá junto com o body da requisição caso o mesmo exista.&lt;/p&gt;

&lt;p&gt;Alguns servidores de storage como &lt;strong&gt;&lt;em&gt;aws.s3&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;cloudinary&lt;/em&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;em&gt;firebase cloud storage&lt;/em&gt;&lt;/strong&gt;, estão preparados para receberem requisições nesse formato, possuindo libs para integração direta em Flutter, dispensando a necessidade da criação de uma api própria.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resolvendo o Oversizing
&lt;/h1&gt;

&lt;p&gt;Algumas estratégias podem ser admitidas a fim de reduzir o oversizing, a mais comum são os famosos thumbnails, algumas alternativas mais recentes que têm grande aplicabilidade a depender do contexto seriam o blurHash e a Dominant color  abaixo explico o funcionamento de ambas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thumbnails
&lt;/h3&gt;

&lt;p&gt;Uma thumbnail é uma imagem comprimida que prevê a imagem original. Elas são mais leves e podem ser utilizadas para diversos fins. Por exemplo, um dos propósitos da utilização dessa thumb é reduzir o tempo de carregamento de alguma pagina na internet, para tornar o acesso mais rápido ao usuário. Então, uma thumbnail de uma foto maior, seria uma versão reduzida dela mesma.&lt;/p&gt;

&lt;p&gt;Em essência, a principal função de uma miniatura é trazer uma informação ao usuário de maneira mais rápida.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://github.com/woltapp/blurhash" rel="noopener noreferrer"&gt;Blurhash&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;“BlurHash is a compact representation of a placeholder for an image”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Blurhash é uma representação compacta de um espaço reservado para uma imagem. Esta é a definição dada pelos desenvolvedores. Trata-se de uma biblioteca que converte uma imagem para uma versão ofuscada e compactada de sua versão.&lt;br&gt;
Essa biblioteca possui implementações para diversas linguagens, typescript, kotlin, swift, python, C, entre outras. Segue abaixo uma visão da mesma.&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%2Fi%2Fhjznmr681n753svu8qh7.JPG" 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%2Fi%2Fhjznmr681n753svu8qh7.JPG" width="528" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dominant color
&lt;/h3&gt;

&lt;p&gt;Trata-se apenas de pegar a cor de maior incidência na imagem, e utilizá-la no local da mesma. Estratégia bastante parecida com o blurHash, a diferença seria apenas o resultado final da imagem.&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%2Fi%2Fnylgnpitcxgk8wpqp1wb.JPG" 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%2Fi%2Fnylgnpitcxgk8wpqp1wb.JPG" width="615" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Referencial Bibliográfico
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://medium.com/snapp-mobile/dont-use-base64-encoded-images-on-mobile-13ddeac89d7c" rel="noopener noreferrer"&gt;https://medium.com/snapp-mobile/dont-use-base64-encoded-images-on-mobile-13ddeac89d7c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pt.wikipedia.org/wiki/Base64" rel="noopener noreferrer"&gt;https://pt.wikipedia.org/wiki/Base64&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/image_picker" rel="noopener noreferrer"&gt;https://pub.dev/packages/image_picker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://medium.com/snapp-mobile/dont-use-base64-encoded-images-on-mobile-13ddeac89d7c" rel="noopener noreferrer"&gt;https://medium.com/snapp-mobile/dont-use-base64-encoded-images-on-mobile-13ddeac89d7c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.coderiddles.com/base64-images/" rel="noopener noreferrer"&gt;http://www.coderiddles.com/base64-images/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://neilpatel.com/br/blog/thumbnail-o-que-e/" rel="noopener noreferrer"&gt;https://neilpatel.com/br/blog/thumbnail-o-que-e/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/woltapp/blurhash" rel="noopener noreferrer"&gt;https://github.com/woltapp/blurhash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://lokeshdhakar.com/projects/color-thief/" rel="noopener noreferrer"&gt;https://lokeshdhakar.com/projects/color-thief/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://flutter.dev/docs/cookbook/images/cached-images" rel="noopener noreferrer"&gt;https://flutter.dev/docs/cookbook/images/cached-images&lt;/a&gt;&lt;/p&gt;

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