DEVLOPN Audit

Flutter 3.19 embarque enfin les assets par flavor, et personne ne l'utilise

Depuis Flutter 3.19, pubspec.yaml sait exclure les assets hors flavor du bundle. Build plus léger sans toucher au code, avec deux limites à connaître.

Une app white-label, cinq marques, cinq flavors. Et dans chaque APK livré : les logos, splash screens et illustrations des quatre autres marques.

Des assets en trop : chaque flavor embarque les images de tous les autres, Flutter 3.19 sait enfin les exclure

C'est le genre de découverte que je fais en décompilant un APK pendant un audit. Personne n'avait rien fait de mal : jusqu'à récemment, Flutter ne proposait tout simplement rien pour ça.

Le problème

pubspec.yaml déclarait tous les assets, les cinq dossiers de marques compris. Le système de flavors (Android product flavors + schemes Xcode) différenciait le code, la config, l'icône. Mais la section assets: de pubspec, elle, ne connaissait pas les flavors : tout partait dans chaque bundle.

Conséquences concrètes : des mégaoctets inutiles par build, et les assets de la marque B lisibles par quiconque décompile l'APK de la marque A. Pour du white-label avec des clients concurrents entre eux, c'est plus gênant qu'une question de poids.

La solution native depuis Flutter 3.19

Depuis Flutter 3.19, une entrée d'asset peut déclarer un sous-champ flavors :

flutter:
  assets:
    - assets/common/
    - path: assets/marque_a/
      flavors:
        - marque_a
    - path: assets/marque_b/
      flavors:
        - marque_b

Au build :

flutter build apk --flavor marque_a

Seuls assets/common/ et assets/marque_a/ partent dans le bundle. Les assets des autres marques n'existent pas dans l'APK. Zéro changement de code Dart : les chemins d'accès restent les mêmes, c'est l'empaquetage qui filtre.

Sur l'app auditée, le gain était immédiat : plusieurs Mo par APK et plus aucune fuite d'assets entre marques.

Les limites à assumer

C'est indexé sur --flavor, et rien d'autre. Pas de condition par --dart-define, pas de variante par environnement (dev/staging/prod) si vos environnements ne sont pas des flavors, pas de filtre par plateforme. Si votre séparation d'assets repose sur autre chose que le flavor de build, ce mécanisme ne vous aidera pas.

Pas de Web, pas de Linux. Le concept de flavor n'existe pas sur ces cibles : la clause flavors y est ignorée et tous les assets partent dans le build. Une app multi-plateforme qui compte sur ce filtrage pour la confidentialité entre marques doit le savoir.

Deux pièges connus. Les chemins d'assets construits dynamiquement au runtime ('assets/$brand/logo.png') passent la compilation puis échouent au chargement si l'asset a été filtré : le filtrage se joue au build, votre code doit refléter le même invariant. Et flutter_gen génère ses accesseurs à partir de la liste complète des assets, sans connaître le flavor : les accesseurs des assets exclus compilent mais pointent dans le vide.

Avant 3.19 : les contournements qu'on peut jeter

Scripts de copie pré-build qui déplaçaient le bon dossier vers assets/active/, étapes CI qui réécrivaient pubspec.yaml, packages tiers de gestion de flavors. Autant de mécanique fragile qui casse au premier flutter pub get lancé sans le script. Si vous en avez encore, c'est l'occasion de simplifier.

À retenir

  • Depuis Flutter 3.19, path: + flavors: dans la section assets excluent du bundle tout ce qui n'appartient pas au flavor courant.
  • Gain double : taille du build et confidentialité des assets entre variantes.
  • Limites : uniquement piloté par --flavor, et inopérant sur Web et Linux.

Vous avez encore un script de copie d'assets dans votre CI ? Il date probablement d'avant 3.19.