<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>FlutterNow</title>
	<atom:link href="https://flutter-now.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://flutter-now.com/</link>
	<description>Le rendez-vous des développeurs Flutter.</description>
	<lastBuildDate>Thu, 30 Oct 2025 16:46:15 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9</generator>

<image>
	<url>https://flutter-now.com/wp-content/uploads/2025/01/cropped-logo-32x32.png</url>
	<title>FlutterNow</title>
	<link>https://flutter-now.com/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">249724562</site>	<item>
		<title>Comment rendre une application Flutter Web optimisée SEO</title>
		<link>https://flutter-now.com/web/comment-rendre-une-application-flutter-web-optimisee-seo/</link>
					<comments>https://flutter-now.com/web/comment-rendre-une-application-flutter-web-optimisee-seo/#comments</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Thu, 30 Oct 2025 10:24:01 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[SEO]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=324</guid>

					<description><![CDATA[<p>Les applications Flutter Web fonctionnent comme des SPA (Single-Page Applications) :au lieu de renvoyer une page HTML complète à chaque navigation, elles chargent une seule [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/web/comment-rendre-une-application-flutter-web-optimisee-seo/">Comment rendre une application Flutter Web optimisée SEO</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Les applications <strong>Flutter Web</strong> fonctionnent comme des <strong>SPA (Single-Page Applications)</strong> :<br>au lieu de renvoyer une page HTML complète à chaque navigation, elles chargent une seule fois un <code>index.html</code> minimal, puis mettent à jour l’interface côté navigateur avec JavaScript.</p>



<p>Ce <code>index.html</code> initial contient très peu d’informations SEO.<br>Mais bonne nouvelle : <strong>Google est capable d’exécuter le JavaScript</strong>, d’attendre que l’application charge, puis <strong>d’indexer le DOM final modifié</strong>.</p>



<p><span style="text-decoration: underline;">Autrement dit </span>:<br>si votre application met à jour dynamiquement coté client le <code>&lt;title&gt;</code>, la meta description ou le contenu des pages, <strong>Google peut lire ces valeurs modifiées</strong>, exactement comme vous les voyez dans le panneau “Elements” du DevTools de Chrome.</p>



<p><span style="text-decoration: underline;">Exemple concret :</span><br>Si vous changez la meta description en JavaScript :</p>



<pre class="wp-block-code"><code>document.querySelector('meta&#91;name="description"]').setAttribute('content', 'Nouvelle description SEO');</code></pre>



<p>Googlebot exécutera le script, verra la meta mise à jour, et pourra l’indexer.</p>



<p>Cependant, pour que le référencement fonctionne réellement bien dans une application Flutter Web, il est indispensable de soigner plusieurs points :</p>



<ul class="wp-block-list">
<li>URLs propres (sans <code>#/</code>)</li>



<li>Mise à jour dynamique des balises <code>&lt;head></code> (title, description, canonical…)</li>



<li>sitemap.xml + robots.txt</li>
</ul>



<p>Voici une <strong>mise en place éprouvée</strong>, simple à intégrer dans un projet Flutter Web en production, afin d’obtenir un comportement SEO comparable à une application web classique.</p>



<h2>1. Avoir des URLs propres (sans #/)</h2>



<p>Par défaut, Flutter Web utilise le hash (<code>/#/page</code>) pour gérer les routes.<br>Ce format fonctionne, mais il n’est <strong>pas optimal pour le SEO</strong>.</p>



<p>On active la <strong>URL Strategy en mode “path”</strong> (vraies URLs lisibles) :</p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'package:flutter_web_plugins/url_strategy.dart';

void main() {
  usePathUrlStrategy(); //enlève #/ dans les URLs
  runApp(const MyApp());
}</code></pre>



<p>Assurez-vous aussi que dans <code>web/index.html</code> la balise <code>&lt;base&gt;</code> est présente :</p>



<pre class="wp-block-code"><code>&lt;base href="/"&gt;</code></pre>



<h2>2. Mettre à jour title et meta en Flutter</h2>



<p>Comme Flutter gère le DOM après chargement, on doit modifier les metas <strong>depuis Dart</strong></p>



<h3>MetaManager (version Web)</h3>



<p><em><mark style="background-color:#abb8c3" class="has-inline-color has-black-color">lib/seo/meta_manager_web.dart</mark></em></p>



<pre class="wp-block-code"><code>import 'dart:html' as html;

class MetaManager {
  static void setTitle(String title) =&gt; html.document.title = title;

  static void setDescription(String description) {
    _upsertMeta('description', description);
  }

  static void setCanonical(String url) {
    _upsertLink(rel: 'canonical', href: url);
  }

  static void _upsertMeta(String name, String content) {
    final el = html.document.head!.querySelector('meta&#91;name="$name"]');
    if (el != null) {
      el.setAttribute('content', content);
      return;
    }
    html.document.head!.append(html.MetaElement()
      ..name = name
      ..content = content);
  }

  static void _upsertLink({required String rel, required String href}) {
    final el = html.document.head!.querySelector('link&#91;rel="$rel"]');
    if (el != null) {
      el.setAttribute('href', href);
      return;
    }
    html.document.head!.append(html.LinkElement()
      ..rel = rel
      ..href = href);
  }
}</code></pre>



<h3>Version mobile (ne rien faire)</h3>



<p><em><mark style="background-color:#abb8c3" class="has-inline-color">lib/seo/meta_manager_stub.dart</mark></em></p>



<pre class="wp-block-code"><code>class MetaManager {
  static void setTitle(String _) {}
  static void setDescription(String _) {}
  static void setCanonical(String _) {}
}</code></pre>



<h3>Import conditionnel</h3>



<p><mark style="background-color:#abb8c3" class="has-inline-color has-black-color">lib/seo/meta_manager.dart</mark></p>



<pre class="wp-block-code"><code>export 'meta_manager_stub.dart'
    if (dart.library.html) 'meta_manager_web.dart';</code></pre>



<p><span style="text-decoration: underline;">Résultat :</span></p>



<ul class="wp-block-list">
<li>Sur Web → les balises <code>&lt;head&gt;</code> sont modifiées</li>



<li>Sur iOS / Android → aucune erreur</li>
</ul>



<h2>3. Appliquer le SEO automatiquement à chaque navigation</h2>



<p>On utilise un <strong>NavigatorObserver</strong> pour mettre à jour les balises dès que l’utilisateur change de page.</p>



<p><mark style="background-color:#abb8c3" class="has-inline-color has-black-color">lib/seo/seo_observer.dart</mark></p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'meta_manager.dart';

class SeoData {
  final String? title;
  final String? description;
  final String? canonicalUrl;

  const SeoData({this.title, this.description, this.canonicalUrl});
}

class SeoObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route&lt;dynamic&gt;? previousRoute) {
    _apply(route);
  }

  void _apply(Route route) {
    final seo = route.settings.arguments;
    if (seo is SeoData) {
      if (seo.title != null) MetaManager.setTitle(seo.title!);
      if (seo.description != null) MetaManager.setDescription(seo.description!);
      if (seo.canonicalUrl != null) MetaManager.setCanonical(seo.canonicalUrl!);
    }
  }
}</code></pre>



<h2>4. Centraliser le SEO dans les routes (GoRouter)</h2>



<p>Chaque route renvoie un <code>MaterialPage</code> avec son SEO associé :</p>



<pre class="wp-block-code"><code>import 'package:go_router/go_router.dart';
import 'seo/seo_observer.dart';
import 'ui/home.dart';
import 'ui/product_list.dart';

const baseUrl = 'https://monsite.com';

final router = GoRouter(
  observers: &#91;SeoObserver()],
  routes: &#91;
    GoRoute(
      path: '/',
      pageBuilder: (context, state) =&gt; MaterialPage(
        key: state.pageKey,
        arguments: const SeoData(
          title: 'Accueil | mon site name',
          description: 'Découvrez notre catalogue, nos offres et nouveautés.',
          canonicalUrl: '$baseUrl/',
        ),
        child: const HomeScreen(),
      ),
    ),
    GoRoute(
      path: 'product-list/:categorie/:type',
      pageBuilder: (context, state) {
        final categorie = state.pathParameters&#91;'categorie']!;
        final type = state.pathParameters&#91;'type']!;
        return MaterialPage(
          key: state.pageKey,
          arguments: SeoData(
            title: '$type - $categorie | Bloopa',
            description: 'Tous les $type dans la catégorie $categorie.',
            canonicalUrl: '$baseUrl/product-list/$categorie/$type',
          ),
          child: ProductListScreen(
            categorie: categorie,
            typeproduit: type,
          ),
        );
      },
    ),
  ],
);
</code></pre>



<p>Avantage : pas besoin d’appeler <code>MetaManager</code> dans chaque page, c’est automatique.</p>



<h2>6. Ajouter un Sitemap et un robots.txt</h2>



<p><mark style="background-color:#abb8c3" class="has-inline-color has-black-color">web/robots.txt</mark></p>



<pre class="wp-block-code"><code>User-agent: *
Allow: /
Sitemap: https://monsite.com/sitemap.xml</code></pre>



<p>La génération du <code>sitemap.xml</code> peut être :</p>



<ul class="wp-block-list">
<li>manuelle</li>



<li>générée à la compilation <mark style="background-color:#cf2e2e" class="has-inline-color has-white-color">(article à venir)</mark></li>



<li>servie par une Cloud Function si catalogue dynamique <mark style="background-color:#cf2e2e" class="has-inline-color has-white-color">(article à venir)</mark></li>
</ul>



<h2>Conclusion</h2>



<p>Avec ces quelques étapes :</p>



<ul class="wp-block-list">
<li>URLs propres</li>



<li>Title / Description / Canonical mis à jour par page</li>



<li>Google indexe le contenu réel affiché au client</li>



<li>Pas de changement côté mobile</li>
</ul>



<p>Comme Google est désormais capable d’exécuter le JavaScript et de lire le contenu final du DOM, une application Flutter Web peut être <strong>réellement SEO-friendly,</strong> à condition que les balises <code>&lt;head&gt;</code> soient mises à jour proprement.</p>



<p><strong><span style="text-decoration: underline;">Note importante :</span></strong> Les bots de <strong>Facebook, Twitter, LinkedIn, WhatsApp, Slack, etc. ne rendent PAS le JavaScript</strong> : ils ne verront donc pas les balises Open Graph ou Twitter Card modifiées côté client.<br>Pour que le partage social affiche correctement l’image, le titre et la description, il faut une stratégie différente, basée sur du <strong>pré-rendu (prerender)</strong> ou une <strong>page HTML statique par URL</strong>.</p>



<p><mark style="background-color:#cf2e2e" class="has-inline-color has-white-color">Un article séparé sera dédié à cette partie “Social Share” pour expliquer comment générer des pages pré-rendues compatibles avec Facebook/Twitter/LinkedIn.</mark></p>



<p></p>
<p>The post <a href="https://flutter-now.com/web/comment-rendre-une-application-flutter-web-optimisee-seo/">Comment rendre une application Flutter Web optimisée SEO</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/web/comment-rendre-une-application-flutter-web-optimisee-seo/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">324</post-id>	</item>
		<item>
		<title>Gestion des métadonnées multilingues pour les stores Apple et Google</title>
		<link>https://flutter-now.com/android/gestion-des-metadonnees-multilingues-pour-les-stores-apple-et-google/</link>
					<comments>https://flutter-now.com/android/gestion-des-metadonnees-multilingues-pour-les-stores-apple-et-google/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Mon, 20 Oct 2025 09:42:07 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Fastlane]]></category>
		<category><![CDATA[ios]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=296</guid>

					<description><![CDATA[<p>Lorsqu’une application mobile est déployée sur plusieurs marchés, la gestion des métadonnées devient un point critique du processus de publication. Chaque store : Google Play, [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/android/gestion-des-metadonnees-multilingues-pour-les-stores-apple-et-google/">Gestion des métadonnées multilingues pour les stores Apple et Google</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Lorsqu’une application mobile est déployée sur plusieurs marchés, la gestion des métadonnées devient un point critique du processus de publication. Chaque store : <strong>Google Play</strong>, <strong>App Store iOS</strong> ou <strong>Mac App Store</strong>, impose ses propres contraintes de structure, de taille, de format et de nommage. Maintenir ces fichiers à jour manuellement pour chaque langue est source d’erreurs. D’où l’intérêt d’une <strong>génération automatisée des métadonnées multilingues</strong>, adossée à une arborescence standardisée et à une logique unique de mappage des locales.</p>



<h3 class="wp-block-heading" id="h-pourquoi-structurer-et-automatiser">Pourquoi structurer et automatiser ?</h3>



<ul class="wp-block-list">
<li>Respect automatique des contraintes de chaque store.</li>



<li>Cohérence entre toutes les langues et tous les marchés.</li>



<li>Processus de mise à jour simplifié lors des releases.</li>



<li>Moins de rejets de publication grâce à une validation en amont.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-arborescence-de-reference-fastlane">Arborescence de référence (Fastlane)</h2>



<p>Référence basée sur la structure réellement utilisée sous <code>build/fastlane/metadata</code>.</p>



<h3 class="wp-block-heading" id="h-android-google-play-store">Android (Google Play Store)</h3>



<pre class="wp-block-code"><code>build/
└── fastlane/
    └── metadata/
        └── android/
            ├── af/
            │   ├── changelogs/
            │   │   ├── 273.txt
            │   │   ├── 274.txt
            │   │   └── 275.txt
            │   ├── images/
            │   ├── full_description.txt
            │   ├── keywords.txt
            │   ├── review_information.txt
            │   ├── short_description.txt
            │   └── title.txt
            ├── am/
            ├── fr-FR/
            └── ...
</code></pre>



<ul class="wp-block-list">
<li><strong>Un dossier par locale</strong> (ex. <code>af</code>, <code>fr-FR</code>, <code>en-US</code>).</li>



<li><code>changelogs/</code> : <strong>un fichier par versionCode</strong> (<code>273.txt</code>, <code>274.txt</code>, …).</li>



<li>Champs texte Google Play : <code>title.txt</code> (≤ 30), <code>short_description.txt</code> (≤ 80), <code>full_description.txt</code> (≤ 4000).</li>



<li><code>keywords.txt</code>, <code>review_information.txt</code> : utiles pour ton pipeline interne (ignorés par Fastlane sans erreur).</li>
</ul>



<h3 class="wp-block-heading" id="h-ios-macos-app-store-connect">iOS / macOS (App Store Connect)</h3>



<pre class="wp-block-code"><code>build/
└── fastlane/
    └── metadata/
        └── ios/
            ├── ar-SA/
            │   ├── description.txt
            │   ├── keywords.txt
            │   ├── marketing_url.txt
            │   ├── name.txt
            │   ├── privacy_url.txt
            │   ├── promotional_text.txt
            │   ├── release_notes.txt
            │   ├── subtitle.txt
            │   └── support_url.txt
            ├── ca/
            ├── de-DE/
            └── ...
</code></pre>



<ul class="wp-block-list">
<li>Champs texte App Store : <code>name.txt</code> (≤ 30), <code>subtitle.txt</code> (≤ 30), <code>description.txt</code> (≤ 4000), <code>keywords.txt</code> (≤ 100, séparés par virgules), <code>promotional_text.txt</code> (≤ 170, optionnel), <code>release_notes.txt</code>.</li>



<li>URLs : <code>support_url.txt</code>, <code>privacy_url.txt</code>, <code>marketing_url.txt</code> (HTTPS valides).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-nommage-des-dossiers-de-langues-locales">Nommage des dossiers de langues (locales)</h2>



<p>Les noms de dossiers doivent respecter les formats officiels des stores, qui diffèrent souvent de ceux utilisés dans le projet (<code>fr</code>, <code>en</code>, <code>pt_BR</code>, etc.). Exemples :</p>



<ul class="wp-block-list">
<li><code>fr</code> → <code>fr-FR</code></li>



<li><code>en</code> → <code>en-US</code></li>



<li><code>pt</code> → <code>pt-BR</code> (Android par défaut dans ton mapping), <code>pt-PT</code> (Apple si nécessaire)</li>



<li><code>zh</code> → <code>zh-CN</code> (Android), <code>zh-Hans</code> (Apple)</li>
</ul>



<p><strong>Règles</strong> :</p>



<ul class="wp-block-list">
<li>Utiliser des <strong>tirets</strong> (<code>-</code>) et non des underscores (<code>_</code>).</li>



<li>Langue en <strong>minuscule</strong>, région en <strong>MAJUSCULE</strong> : <code>en-US</code>.</li>



<li>Ne créer de dossiers que pour des locales <strong>supportées</strong> par le store.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-bibliotheque-dart-mappage-des-locales-source-de-verite">Bibliothèque Dart — mappage des locales (source de vérité)</h2>



<p>Ce bloc gère la conversion des locales du projet vers les formats attendus par <strong>App Store Connect</strong> et <strong>Google Play</strong>. Collez-le tel quel dans votre base de code.</p>



<pre class="wp-block-code"><code>/// Bibliothèque partagée pour mapper les locales du projet aux formats
/// attendus par le Google Play Store et l'App Store Connect.
/// C'est la source de vérité unique pour la logique de mappage.

// ===========================================================================
// LOGIQUE POUR APP STORE CONNECT (IOS)
// ===========================================================================

/// Mappage des locales du projet vers les locales spécifiques à Apple.
const Map&amp;lt;String, String&amp;gt; _appleLocaleMapping = {
  'en': 'en-US', 'en-GB': 'en-GB', 'en-CA': 'en-CA', 'fr': 'fr-FR', 'fr-CA': 'fr-CA',
  'es': 'es-ES', 'es-MX': 'es-MX', 'de': 'de-DE', 'it': 'it',
  'pt': 'pt-PT', // Spécifique à Apple : Portugais (Portugal)
  'pt-BR': 'pt-BR', 'zh': 'zh-Hans', 'zh-CN': 'zh-Hans', 'zh-HK': 'zh-Hant',
  'zh-TW': 'zh-Hant', 'ja': 'ja', 'ko': 'ko', 'ru': 'ru', 'ar': 'ar-SA',
  'nl': 'nl-NL', 'sv': 'sv', 'fi': 'fi', 'da': 'da', 'no': 'no', 'tr': 'tr',
  'pl': 'pl', 'id': 'id', 'th': 'th', 'vi': 'vi', 'he': 'he', 'ms': 'ms',
  'ro': 'ro', 'cs': 'cs', 'sk': 'sk', 'hr': 'hr', 'uk': 'uk', 'hi': 'hi',
  'el': 'el', 'ca': 'ca', 'et-EE': 'et', 'uk-UA': 'uk',
};

/// Liste des locales officiellement supportées par deliver/App Store Connect.
const Set&amp;lt;String&amp;gt; _appStoreSupportedLocales = {
  'ar-SA', 'ca', 'cs', 'da', 'de-DE', 'el', 'en-AU', 'en-CA', 'en-GB', 'en-US',
  'es-ES', 'es-MX', 'fi', 'fr-CA', 'fr-FR', 'he', 'hi', 'hr', 'hu', 'id', 'it',
  'ja', 'ko', 'ms', 'nl-NL', 'no', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk',
  'sv', 'th', 'tr', 'uk', 'vi', 'zh-Hans', 'zh-Hant', 'et'
};

/// Convertit une locale du projet en sa version pour l'App Store.
String? toAppleLocale(String locale) {
  if (_appleLocaleMapping.containsKey(locale)) {
    final appleLocale = _appleLocaleMapping&#91;locale]!;
    // Si la locale mappée est supportée, on la retourne, sinon, échec.
    return _appStoreSupportedLocales.contains(appleLocale) ? appleLocale : null;
  }
  if (_appStoreSupportedLocales.contains(locale)) return locale;
  return null; // Fallback : la locale n'est pas supportée.
}

/// ===========================================================================
// LOGIQUE POUR GOOGLE PLAY (ANDROID) - CORRIGÉ
// ===========================================================================

/// Mappage des locales du projet vers les locales spécifiques à Google Play.
/// CORRIGÉ : Cette table est maintenant basée sur la liste officielle de votre
/// fiche Play Store. Elle résout les ambiguïtés (ex: 'fr' -&amp;gt; 'fr-FR') et
/// mappe les codes simples vers eux-mêmes si c'est le format attendu.
const Map&amp;lt;String, String&amp;gt; _playLocaleMapping = {
  // Mappages directs (le code simple est le format attendu)
  'af': 'af',  'am': 'am', 'bg': 'bg', 'ca': 'ca','lv': 'lv',
  'hr': 'hr',  'et': 'et',  'el': 'el-GR',
  'lt': 'lt', 'ms': 'ms',  'ro': 'ro',
   'sr': 'sr', 'sk': 'sk', 'sl': 'sl',  'sw': 'sw',
  'fil': 'fil', 'cs': 'cs-CZ', 'th': 'th',  'uk': 'uk', 'vi': 'vi',
  'zu': 'zu','gu':'gu','kk':'kk','pa':'pa','sq':'sq','ur':'ur',
  'ar':'ar',
  // Gestion de l'Indonésien (id/in)
  'id': 'id',
  'in': 'id', // 'in' est un ancien code pour 'id'

  // Mappages pour résoudre les ambiguïtés ou suivre des règles spécifiques
  'fr': 'fr-FR', // Instruction explicite : fr -&amp;gt; fr-FR
  'en': 'en-US', // Défaut pour 'en'
  'es': 'es-ES', // Défaut pour 'es'
  'pt': 'pt-BR', // Défaut pour 'pt'
  'zh': 'zh-CN', // Défaut pour 'zh'
  'sv': 'sv-SE',
  'ta' : 'ta-IN',
  'te': 'te-IN', // Défaut pour 'ta-IN"
  'tr': 'tr-TR',
  'de': 'de-DE',
  'fi': 'fi-FI',
  'hi': 'hi-IN',
  'hu': 'hu-HU',
  'is': 'is-IS',
  'it': 'it-IT',
  'ja': 'ja-JP',
  'ko': 'ko-KR',
  'nl': 'nl-NL',
  'no' :'no-NO',
  'pl': 'pl-PL',
  'ru': 'ru-RU',
  'da': 'da-DK',
  'az':"az-AZ",
  'be':'be',
  'bn':'bn-BD',
  'eu':'eu-ES',
  'fa':'fa-IR',
  'gl':'gl-ES',
  'he':"iw-IL",
  'hy':"hy-AM",
  'ka':'ka-GE',
  'km':'km-KH',
  'kn':'kn-IN',
  'ky':'ky-KG',
  'lo':'lo-LA',
  'mk':'mk-MK',
  'ml':'ml-in',
  'mn':'mn-MN',
  'mr':'mr-in',
  'my':'my-MM',
  'ne':'ne-NP',
  'si':'si-LK',
  // Mappages régionaux déjà corrects et supportés
  'en-US': 'en-US', 'en-GB': 'en-GB', 'zh-HK': 'zh-HK', 'zh-CN': 'zh-CN',
  'zh-TW': 'zh-TW', 'es-419': 'es-419', 'es-ES': 'es-ES', 'fr-CA': 'fr-CA',
  'fr-FR': 'fr-FR', 'pt-BR': 'pt-BR', 'pt-PT': 'pt-PT',
};

/// CORRIGÉ : Liste des locales officiellement supportées par VOTRE Google Play Store.
const Set&amp;lt;String&amp;gt; _playSupportedLocales = {
  'be','ar','af','az', 'de', 'am', 'en-US', 'en-GB', 'bg', 'ca', 'zh-HK', 'zh-CN', 'zh-TW',
  'ko', 'hr', 'da', 'es-419', 'es-ES', 'et', 'fi', 'fr-CA', 'fr-FR', 'el','si',
   'hi', 'hu', 'id', 'in', 'is', 'it', 'ja', 'lv', 'lt', 'ms', 'nl','mk','my','ne',
  'no', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sr', 'sk', 'sl', 'sv', 'sw','ka','km','kn','ky','lo','mn','mr',
  'fil', 'cs', 'th', 'tr', 'uk', 'vi', 'zu','gu','kk','pa','sq','ta','te','ur','bn','eu','fa','gl','he','hy','ml'
};

/// Convertit une locale du projet en sa version pour le Google Play Store.
String? toPlayLocale(String locale) {
  // Normalise la locale (ex: ja_JP -&amp;gt; ja-JP)
  final normalizedLocale = locale.replaceAll('_', '-');

  // 1. Mappage explicite (ex: 'ja' -&amp;gt; 'ja-JP')
  if (_playLocaleMapping.containsKey(normalizedLocale)) {
    return _playLocaleMapping&#91;normalizedLocale];
  }

  // 2. Locale déjà supportée telle quelle (ex: 'fr-CA')
  if (_playSupportedLocales.contains(normalizedLocale)) {
    return normalizedLocale;
  }

  // 3. Non supportée
  return null;
}
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-exemple-d-utilisation-creation-automatique-des-dossiers">Exemple d’utilisation : création automatique des dossiers</h2>



<p>Exemple minimal pour générer les dossiers de langues conformes sous <code>build/fastlane/metadata</code> :</p>



<pre class="wp-block-code"><code>import 'dart:io';

void main() {
  final locales = &#91;'af', 'fr', 'en', 'pt-BR', 'zh'];
  for (final locale in locales) {
    final ios = toAppleLocale(locale);
    final play = toPlayLocale(locale);

    if (ios != null) {
      Directory('build/fastlane/metadata/ios/$ios').createSync(recursive: true);
      print('&#x2705; Dossier iOS créé : build/fastlane/metadata/ios/$ios');
    }
    if (play != null) {
      Directory('build/fastlane/metadata/android/$play').createSync(recursive: true);
      Directory('build/fastlane/metadata/android/$play/changelogs').createSync(recursive: true);
      print('&#x2705; Dossier Android créé : build/fastlane/metadata/android/$play');
    }
  }
}
</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-contraintes-de-contenu-rappel">Contraintes de contenu (rappel)</h2>



<h3 class="wp-block-heading">Google Play Store</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Fichier</th><th>Description</th><th>Limite / Format</th></tr></thead><tbody><tr><td><code>title.txt</code></td><td>Titre de l’application</td><td>≤ 30 caractères</td></tr><tr><td><code>short_description.txt</code></td><td>Description courte affichée dans le store</td><td>≤ 80 caractères</td></tr><tr><td><code>full_description.txt</code></td><td>Description complète de la fiche Google Play</td><td>≤ 4000 caractères</td></tr><tr><td><code>changelogs/&lt;versionCode&gt;.txt</code></td><td>Notes de version spécifiques à chaque build</td><td>Un fichier par versionCode (ex. <code>273.txt</code>)</td></tr><tr><td><code>images/</code></td><td>Visuels : icône, feature graphic, captures d’écran</td><td>icon 512×512 px, featureGraphic 1024×500 px</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">App Store (iOS / macOS)</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Fichier</th><th>Description</th><th>Limite / Format</th></tr></thead><tbody><tr><td><code>name.txt</code></td><td>Nom de l’application</td><td>≤ 30 caractères</td></tr><tr><td><code>subtitle.txt</code></td><td>Sous-titre affiché dans la fiche App Store</td><td>≤ 30 caractères</td></tr><tr><td><code>description.txt</code></td><td>Description complète de la fiche</td><td>≤ 4000 caractères</td></tr><tr><td><code>keywords.txt</code></td><td>Mots-clés pour le référencement interne</td><td>≤ 100 caractères, séparés par virgules</td></tr><tr><td><code>promotional_text.txt</code></td><td>Texte promotionnel temporaire</td><td>≤ 170 caractères (optionnel)</td></tr><tr><td><code>release_notes.txt</code></td><td>Notes de version associées à la build</td><td>Texte libre</td></tr><tr><td><code>support_url.txt</code></td><td>Lien vers le support utilisateur</td><td>URL HTTPS valide</td></tr><tr><td><code>privacy_url.txt</code></td><td>Politique de confidentialité</td><td>URL HTTPS valide</td></tr><tr><td><code>marketing_url.txt</code></td><td>Page de marketing externe</td><td>URL HTTPS valide (optionnel)</td></tr></tbody></table></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-bonnes-pratiques">Bonnes pratiques</h2>



<ul class="wp-block-list">
<li>Utiliser des <strong>tirets</strong> (<code>-</code>) pour les locales (ex. <code>fr-FR</code>), jamais d’underscores.</li>



<li>Langue en minuscule, région en MAJUSCULE (ex. <code>en-US</code>).</li>



<li>Valider la <strong>taille des champs</strong> avant publication.</li>



<li>Centraliser les textes (JSON, CSV, <code>.arb</code>) et générer automatiquement.</li>



<li>Ne créer des dossiers que pour des locales <strong>supportées</strong> par le store.</li>
</ul>



<p><em>Avec cette structure et ce mappage, la génération multilingue des métadonnées devient fiable, cohérente et parfaitement compatible avec Fastlane pour Google Play et App Store.</em></p>
<p>The post <a href="https://flutter-now.com/android/gestion-des-metadonnees-multilingues-pour-les-stores-apple-et-google/">Gestion des métadonnées multilingues pour les stores Apple et Google</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/android/gestion-des-metadonnees-multilingues-pour-les-stores-apple-et-google/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">296</post-id>	</item>
		<item>
		<title>Arborescence des screenshots multilingues – App Store &#038; Play Store</title>
		<link>https://flutter-now.com/android/arborescence-des-screenshots-multilingues-app-store-play-store/</link>
					<comments>https://flutter-now.com/android/arborescence-des-screenshots-multilingues-app-store-play-store/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Thu, 16 Oct 2025 09:59:34 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Fastlane]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[Apple Store]]></category>
		<category><![CDATA[Déploiement]]></category>
		<category><![CDATA[Google Play Store]]></category>
		<category><![CDATA[IOS]]></category>
		<category><![CDATA[Multilingue]]></category>
		<category><![CDATA[Screenshots]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=265</guid>

					<description><![CDATA[<p>Les captures d’écran sont un élément essentiel de la présentation d’une application sur les stores mobiles.Elles influencent directement la conversion, la crédibilité et la qualité [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/android/arborescence-des-screenshots-multilingues-app-store-play-store/">Arborescence des screenshots multilingues – App Store &amp; Play Store</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Les <strong>captures d’écran</strong> sont un élément essentiel de la présentation d’une application sur les stores mobiles.<br>Elles influencent directement la <strong>conversion</strong>, la <strong>crédibilité</strong> et la <strong>qualité perçue</strong> de ton produit.<br>Pour garantir une diffusion fluide et conforme, il est crucial d’adopter une <strong>arborescence structurée</strong>, des <strong>tailles cohérentes</strong>, et des <strong>captures localisées</strong> pour chaque langue.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607" crossorigin="anonymous"></script>
<div class="adsense-wrapper"><ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="1661698549"></ins></div>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h2>Structure générale</h2>



<p>Chaque store a ses propres règles :</p>



<ul class="wp-block-list">
<li><strong>Play Store (Android)</strong> : captures d’écran classées par <strong>type d’appareil</strong>, avec des <strong>tailles et densités précises</strong>.</li>



<li><strong>App Store (iOS)</strong> : captures classées par <strong>format d’écran Apple</strong>, générées via les <strong>simulateurs officiels Xcode</strong></li>



<li><strong>Multilingue</strong> → chaque langue possède son propre dossier (<code>fr-FR</code>, <code>en-US</code>, <code>es-ES</code>, etc.).</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2>Play Store (Android)</h2>



<p>Arborescence type</p>



<pre class="wp-block-code"><code>/android/
├── fr-FR/
│   └── images/
│       ├── FeatureGraphic.png
│       ├── phoneScreenshots/
│       │   ├── 01_home.png
│       │   ├── 02_features.png
│       │   └── ...
│       ├── sevenInchScreenshots/
│       │   ├── 01_home.png
│       │   └── ...
│       └── tenInchScreenshots/
│           ├── 01_home.png
│           └── ...
│
└── en-US/
    └── images/
        ├── FeatureGraphic.png
        ├── phoneScreenshots/
        ├── sevenInchScreenshots/
        └── tenInchScreenshots/
</code></pre>



<h3>Le Feature Graphic</h3>



<p>Le <code>FeatureGraphic.png</code> est <strong>le visuel principal du Play Store</strong>.<br>Il s’affiche en haut de la fiche produit et dans certaines zones promotionnelles du store.</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>Élement</strong></td><td><strong>Taille (px)</strong></td><td><strong>Densité (dpi)</strong></td></tr><tr><td>FeatureGraphic</td><td>1024×500</td><td>160</td></tr></tbody></table></figure>



<p>C’est souvent <strong>le premier visuel vu par l’utilisateur</strong> : soigne le design, évite les textes trop petits et garde une cohérence visuelle avec tes screenshots.</p>



<h3>Tailles et densités recommandées pour les screenshots Android</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>Type d’appareil</strong></td><td><strong>Dossier</strong></td><td><strong>Taille (px)</strong></td><td><strong>Densité (dpi)</strong></td><td><strong>Max screenshots</strong></td></tr><tr><td>Téléphone</td><td>phoneScreenshots/</td><td>1080×1920</td><td>420</td><td>8</td></tr><tr><td>Tablette 7&Prime;</td><td>sevenInchScreenshots/</td><td>800×1280</td><td>213</td><td>8</td></tr><tr><td>Tablette 10&Prime;</td><td>tenInchScreenshots/</td><td>1600×2560</td><td>320</td><td>8</td></tr></tbody></table></figure>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Si tu dépasses 8 captures par dossier, <strong>Google Play bloquera le déploiement</strong></p>



<h3>Nommage des fichiers</h3>



<p>Le Play Store trie les fichiers <strong>par ordre alphabétique</strong>.<br>Pour contrôler l’ordre d’affichage, <strong>numérote systématiquement les fichiers</strong></p>



<pre class="wp-block-code"><code>01_home.png
02_features.png
03_profile.png
04_settings.png</code></pre>



<p>Les préfixes numériques (<code>01_</code>, <code>02_</code>, etc.) sont <strong>obligatoires</strong> pour garantir une séquence visuelle cohérente.</p>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607" crossorigin="anonymous"></script>
<div class="adsense-wrapper">
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="6970817239"></ins>
</div>
<script>
  (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2>App Store (iOS)</h2>



<h3>Arborescence type</h3>



<pre class="wp-block-code"><code>/ios/
├── fr-FR/
│   └── images/
│       ├── iPhone 6.7-inch_01.png
│       ├── iPhone 6.7-inch_02.png
│       ├── iPad Pro (12.9-inch) (3rd generation)_01.png
│       ├── iPad Pro (12.9-inch) (3rd generation)_02.png
│       └── ...
│
└── en-US/
    └── images/
        ├── iPhone 6.7-inch_01.png
        ├── iPad Pro (12.9-inch) (3rd generation)_01.png
        └── ...
</code></pre>



<h3>Limites et méthode de génération</h3>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>Type d’appareil</strong></td><td><strong>Méthode recommandée</strong></td><td><strong>Taille native</strong></td><td><strong>Max screenshots</strong></td></tr><tr><td>iPhone 6.7-inch</td><td>Simulateur Xcode officiel</td><td>générée automatiquement</td><td>10</td></tr><tr><td>iPad Pro (12.9-inch) (3rd generation)</td><td>Simulateur Xcode officiel</td><td>générée automatiquement</td><td>10</td></tr></tbody></table></figure>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> N’utilise <strong>jamais de captures redimensionnées manuellement</strong> : Apple exige des fichiers générés depuis les simulateurs correspondant à la taille d’écran.</p>



<h3>Astuce</h3>



<p>Les formats <code>iPhone 6.7-inch</code> et <code>iPad Pro (12.9-inch)</code> couvrent <strong>presque tous les appareils iOS modernes</strong>.</p>



<h3>Nommage des fichiers</h3>



<pre class="wp-block-code"><code>iPhone 6.7-inch_01.png
iPad Pro (12.9-inch) (3rd generation)_01.png</code></pre>



<p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2757.png" alt="❗" class="wp-smiley" style="height: 1em; max-height: 1em;" />Respecte scrupuleusement la casse, les espaces et les parenthèses.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2>Structure multilingue complète</h2>



<pre class="wp-block-code"><code>/screenshots/
├── android/
│   ├── fr-FR/
│   ├── en-US/
│   └── es-ES/
└── ios/
    ├── fr-FR/
    ├── en-US/
    └── es-ES/</code></pre>



<h3>Importance des screenshots localisés</h3>



<p>Les stores affichent automatiquement les images selon la <strong>langue de l’utilisateur</strong>.<br>Créer des captures <strong>localisées et traduites</strong> améliore la compréhension et la conversion.</p>



<div class="adsense-wrapper">
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="9812796514"></ins></div>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h3>Mappage des locales (langues et dossiers)</h3>



<p>Le <strong>mappage des locales</strong> est la brique clé qui garantit que les <strong>screenshots générés automatiquement</strong> correspondent aux <strong>formats de langues réellement supportés</strong> par le store sur lequel tu publie</p>



<p><strong>Pourquoi ce mappage est essentiel</strong></p>



<ul class="wp-block-list">
<li>Il permet de <strong>convertir les locales du projet (.arb)</strong> vers les <strong>formats de locales officiels</strong> utilisés par chaque store (ex. <code>fr</code> → <code>fr-FR</code>).</li>



<li>Il sert lors de la <strong>génération automatique des dossiers de screenshots localisés</strong> pour les outils comme <strong>Fastlane</strong>, <strong>CI/CD</strong>, ou les scripts internes.</li>



<li>Il évite les erreurs de build et de déploiement dues à une langue non supportée sur la plateforme cible.</li>



<li><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Les listes de langues supportées <strong>diffèrent entre le Play Store et l’App Store</strong>.
<ul class="wp-block-list">
<li>Par exemple :
<ul class="wp-block-list">
<li>Google Play accepte <code>es-419</code> (Espagnol Amérique latine), <strong>non supporté par Apple</strong>.</li>



<li>L’App Store supporte <code>fr-CA</code> (Français Canada), <strong>non reconnu directement par Google Play</strong>.</li>
</ul>
</li>
</ul>
</li>
</ul>



<p>Ce mappage garantit que tu génères uniquement des dossiers de langues compatibles avec <strong>le store cible</strong>.</p>



<h3>Code source complet du mappage des locales</h3>



<pre class="wp-block-code"><code>/// Bibliothèque partagée pour mapper les locales du projet aux formats
/// attendus par le Google Play Store et l'App Store Connect.
/// C'est la source de vérité unique pour la logique de mappage.

// ===========================================================================
// LOGIQUE POUR APP STORE CONNECT (IOS)
// ===========================================================================

const Map&lt;String, String&gt; _appleLocaleMapping = {
  'en': 'en-US', 'en-GB': 'en-GB', 'en-CA': 'en-CA', 'fr': 'fr-FR', 'fr-CA': 'fr-CA',
  'es': 'es-ES', 'es-MX': 'es-MX', 'de': 'de-DE', 'it': 'it',
  'pt': 'pt-PT', 'pt-BR': 'pt-BR', 'zh': 'zh-Hans', 'zh-CN': 'zh-Hans', 'zh-HK': 'zh-Hant',
  'zh-TW': 'zh-Hant', 'ja': 'ja', 'ko': 'ko', 'ru': 'ru', 'ar': 'ar-SA',
  'nl': 'nl-NL', 'sv': 'sv', 'fi': 'fi', 'da': 'da', 'no': 'no', 'tr': 'tr',
  'pl': 'pl', 'id': 'id', 'th': 'th', 'vi': 'vi', 'he': 'he', 'ms': 'ms',
  'ro': 'ro', 'cs': 'cs', 'sk': 'sk', 'hr': 'hr', 'uk': 'uk', 'hi': 'hi',
  'el': 'el', 'ca': 'ca', 'et-EE': 'et', 'uk-UA': 'uk',
};

const Set&lt;String&gt; _appStoreSupportedLocales = {
  'ar-SA', 'ca', 'cs', 'da', 'de-DE', 'el', 'en-AU', 'en-CA', 'en-GB', 'en-US',
  'es-ES', 'es-MX', 'fi', 'fr-CA', 'fr-FR', 'he', 'hi', 'hr', 'hu', 'id', 'it',
  'ja', 'ko', 'ms', 'nl-NL', 'no', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk',
  'sv', 'th', 'tr', 'uk', 'vi', 'zh-Hans', 'zh-Hant', 'et'
};

String? toAppleLocale(String locale) {
  if (_appleLocaleMapping.containsKey(locale)) {
    final appleLocale = _appleLocaleMapping&#91;locale]!;
    return _appStoreSupportedLocales.contains(appleLocale) ? appleLocale : null;
  }
  if (_appStoreSupportedLocales.contains(locale)) return locale;
  return null;
}

// ===========================================================================
// LOGIQUE POUR GOOGLE PLAY (ANDROID)
// ===========================================================================

const Map&lt;String, String&gt; _playLocaleMapping = {
  'af': 'af', 'am': 'am', 'bg': 'bg', 'ca': 'ca','lv': 'lv',
  'hr': 'hr', 'et': 'et', 'el': 'el-GR', 'lt': 'lt', 'ms': 'ms', 'ro': 'ro',
  'sr': 'sr', 'sk': 'sk', 'sl': 'sl', 'sw': 'sw', 'fil': 'fil', 'cs': 'cs-CZ',
  'th': 'th', 'uk': 'uk', 'vi': 'vi', 'zu': 'zu', 'gu': 'gu', 'kk': 'kk', 'pa': 'pa',
  'sq': 'sq', 'ur': 'ur', 'ar': 'ar', 'id': 'id', 'in': 'id',
  'fr': 'fr-FR', 'en': 'en-US', 'es': 'es-ES', 'pt': 'pt-BR', 'zh': 'zh-CN',
  'sv': 'sv-SE', 'ta': 'ta-IN', 'te': 'te-IN', 'tr': 'tr-TR', 'de': 'de-DE',
  'fi': 'fi-FI', 'hi': 'hi-IN', 'hu': 'hu-HU', 'is': 'is-IS', 'it': 'it-IT',
  'ja': 'ja-JP', 'ko': 'ko-KR', 'nl': 'nl-NL', 'no': 'no-NO', 'pl': 'pl-PL',
  'ru': 'ru-RU', 'da': 'da-DK', 'az': 'az-AZ', 'be': 'be', 'bn': 'bn-BD',
  'eu': 'eu-ES', 'fa': 'fa-IR', 'gl': 'gl-ES', 'he': 'iw-IL', 'hy': 'hy-AM',
  'ka': 'ka-GE', 'km': 'km-KH', 'kn': 'kn-IN', 'ky': 'ky-KG', 'lo': 'lo-LA',
  'mk': 'mk-MK', 'ml': 'ml-in', 'mn': 'mn-MN', 'mr': 'mr-in', 'my': 'my-MM',
  'ne': 'ne-NP', 'si': 'si-LK', 'en-US': 'en-US', 'en-GB': 'en-GB', 'zh-HK': 'zh-HK',
  'zh-CN': 'zh-CN', 'zh-TW': 'zh-TW', 'es-419': 'es-419', 'es-ES': 'es-ES',
  'fr-CA': 'fr-CA', 'fr-FR': 'fr-FR', 'pt-BR': 'pt-BR', 'pt-PT': 'pt-PT',
};

const Set&lt;String&gt; _playSupportedLocales = {
  'be','ar','af','az','de','am','en-US','en-GB','bg','ca','zh-HK','zh-CN','zh-TW',
  'ko','hr','da','es-419','es-ES','et','fi','fr-CA','fr-FR','el','si','hi','hu','id','in',
  'is','it','ja','lv','lt','ms','nl','mk','my','ne','no','pl','pt-BR','pt-PT','ro','ru',
  'sr','sk','sl','sv','sw','ka','km','kn','ky','lo','mn','mr','fil','cs','th','tr','uk',
  'vi','zu','gu','kk','pa','sq','ta','te','ur','bn','eu','fa','gl','he','hy','ml'
};

String? toPlayLocale(String locale) {
  final normalizedLocale = locale.replaceAll('_', '-');
  if (_playLocaleMapping.containsKey(normalizedLocale)) {
    return _playLocaleMapping&#91;normalizedLocale];
  }
  if (_playSupportedLocales.contains(normalizedLocale)) {
    return normalizedLocale;
  }
  return null;
}</code></pre>



<h3>Bonnes pratiques</h3>



<ul class="wp-block-list">
<li><strong>Maintiens ton mappage des locales à jour</strong> (<code>locale_mapping.dart</code>) pour générer des screenshots uniquement dans les langues réellement supportées par chaque store.</li>



<li><strong>Respecte le format des dossiers de langues</strong> (<code>fr-FR</code>, <code>en-US</code>, etc.).</li>



<li><strong>Numérote les fichiers Android</strong> (<code>01_</code>, <code>02_</code>, etc.).</li>



<li><strong>Utilise les simulateurs Xcode officiels</strong> pour iOS.</li>



<li>Sur iOS, <strong>limite-toi à <code>iPhone 6.7-inch</code> et <code>iPad Pro (12.9-inch)</code></strong> pour couvrir la majorité des cas.</li>



<li><strong>Soigne ton Feature Graphic</strong> : c’est souvent le premier visuel vu par l’utilisateur.</li>



<li><strong>Génère des captures localisées</strong> pour chaque langue supportée.</li>



<li><strong>Ne dépasse jamais les limites</strong> :
<ul class="wp-block-list">
<li>8 images max / dossier Android</li>



<li>10 images max / format iOS</li>
</ul>
</li>



<li><strong>Teste toujours tes visuels sur les devices correspondants</strong> avant déploiement.</li>
</ul>



<div class="adsense-wrapper"><ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="3470362404"></ins></div>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
<p>The post <a href="https://flutter-now.com/android/arborescence-des-screenshots-multilingues-app-store-play-store/">Arborescence des screenshots multilingues – App Store &amp; Play Store</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/android/arborescence-des-screenshots-multilingues-app-store-play-store/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">265</post-id>	</item>
		<item>
		<title>Flutter Driver : Le guide complet pour automatiser vos tests d’intégration Flutter</title>
		<link>https://flutter-now.com/tests-et-qualite/flutter-driver-le-guide-complet-pour-automatiser-vos-tests-dintegration-flutter/</link>
					<comments>https://flutter-now.com/tests-et-qualite/flutter-driver-le-guide-complet-pour-automatiser-vos-tests-dintegration-flutter/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Wed, 15 Oct 2025 13:07:46 +0000</pubDate>
				<category><![CDATA[Tests et Qualité]]></category>
		<category><![CDATA[Automatisation des tests]]></category>
		<category><![CDATA[CI/CD]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Flutter Driver]]></category>
		<category><![CDATA[QA mobile]]></category>
		<category><![CDATA[Qualité logicielle]]></category>
		<category><![CDATA[Test automatisé]]></category>
		<category><![CDATA[Test d’intégration]]></category>
		<category><![CDATA[Tests end-to-end]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=254</guid>

					<description><![CDATA[<p>Introduction Dans le monde du développement mobile, livrer une application sans bugs et performante est un véritable défi.Que vous travailliez sur un projet personnel ou [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/tests-et-qualite/flutter-driver-le-guide-complet-pour-automatiser-vos-tests-dintegration-flutter/">Flutter Driver : Le guide complet pour automatiser vos tests d’intégration Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2>Introduction</h2>



<p>Dans le monde du développement mobile, livrer une application sans bugs et performante est un véritable défi.<br>Que vous travailliez sur un projet personnel ou une application d’entreprise, vous avez besoin de garantir que <strong>chaque fonctionnalité fonctionne correctement et ensemble</strong>.</p>



<p>C’est là qu’intervient <strong>Flutter Driver</strong> : un outil puissant conçu pour <strong>automatiser les tests d’intégration</strong> et reproduire les actions d’un utilisateur réel dans votre application Flutter.</p>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607" crossorigin="anonymous"></script>
<div class="adsense-wrapper"><ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="1661698549"></ins></div>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h2>Qu’est-ce que Flutter Driver ?</h2>



<p><strong>Flutter Driver</strong> est un framework officiel de test d’intégration développé par l’équipe Flutter.<br>Il permet d’exécuter des <strong>tests end-to-end (E2E)</strong>, c’est-à-dire des tests qui <strong>simulent les interactions d’un utilisateur</strong> — appuyer sur des boutons, faire défiler des listes, saisir du texte, ou encore naviguer entre les écrans.</p>



<p>En d’autres mots : si les <strong>tests unitaires</strong> vérifient que chaque pièce du puzzle fonctionne, <strong>Flutter Driver</strong> s’assure que le puzzle entier est bien assemblé.</p>



<h2>Pourquoi utiliser Flutter Driver ?</h2>



<p>Mettre en place des tests Flutter Driver, c’est faire un pas vers la <strong>qualité logicielle</strong> et la <strong>fiabilité à long terme</strong>.<br>Voici les principaux avantages :</p>



<ul class="wp-block-list">
<li><strong>Automatisation totale</strong> : simule les gestes réels d’un utilisateur.</li>



<li><strong>Détection rapide des erreurs d’intégration</strong> entre widgets ou écrans.</li>



<li><strong>Intégration continue (CI/CD)</strong> : compatible avec GitHub Actions, GitLab CI, ou Jenkins.</li>



<li><strong>Mesure des performances</strong> : permet d’évaluer le temps de rendu et la fluidité de votre app.</li>
</ul>



<h2>Structure typique d’un test Flutter Driver</h2>



<p>Un test Flutter Driver s’organise généralement autour de <strong>deux fichiers clés</strong> :</p>



<ol class="wp-block-list">
<li><strong><code>test_driver/app.dart</code></strong> → Lance l’application en mode test.</li>



<li><strong><code>test_driver/app_test.dart</code></strong> → Contient le scénario de test automatisé.</li>
</ol>



<p>Exemple d’arborescence :</p>



<pre class="wp-block-code"><code>my_app/
├── lib/
│   └── main.dart
├── test_driver/
│   ├── app.dart
│   └── app_test.dart</code></pre>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607" crossorigin="anonymous"></script>
<div class="adsense-wrapper">
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="6970817239"></ins>
</div>
<script>
  (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h2>Exemple complet de test Flutter Driver</h2>



<h3>1. Fichier test_driver/app.dart</h3>



<p>Ce fichier démarre l’application avec l’extension de test activée.</p>



<pre class="wp-block-code"><code>import 'package:flutter_driver/driver_extension.dart';
import 'package:my_app/main.dart' as app;

void main() {
  enableFlutterDriverExtension();
  app.main();
}
</code></pre>



<h3>2. Fichier test_driver/app_test.dart</h3>



<p>Ici, on écrit un scénario d’interaction automatisé :</p>



<pre class="wp-block-code"><code>import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';

void main() {
  group('Test Flutter Driver', () {
    FlutterDriver? driver;

    setUpAll(() async {
      driver = await FlutterDriver.connect();
    });

    tearDownAll(() async {
      if (driver != null) {
        driver!.close();
      }
    });

    test('Vérifie le clic sur un bouton', () async {
      final button = find.byValueKey('increment');
      final counter = find.byValueKey('counter');

      expect(await driver!.getText(counter), "0");
      await driver!.tap(button);
      expect(await driver!.getText(counter), "1");
    });
  });
}
</code></pre>



<h3>Exécution du test</h3>



<p>Pour lancer le test, il suffit d’exécuter la commande suivante dans votre terminal</p>



<pre class="wp-block-code"><code>flutter drive --target=test_driver/app.dart</code></pre>



<p>Flutter compilera l’application, la déploiera sur un simulateur ou un appareil réel, puis exécutera automatiquement vos scénarios de test.</p>



<h2>Bonnes pratiques pour vos tests Flutter Driver</h2>



<ul class="wp-block-list">
<li>Utilisez des <strong>ValueKey</strong> dans vos widgets pour identifier facilement les éléments.</li>



<li><strong>Évitez de tester la logique métier</strong> ici, concentrez-vous sur les interactions utilisateur.</li>



<li><strong>Automatisez vos tests</strong> dans un pipeline CI/CD.</li>



<li><strong>Nettoyez vos données</strong> et préparez un environnement stable avant chaque test.</li>



<li><strong>Mesurez les performances</strong> pour détecter d’éventuels ralentissements</li>
</ul>



<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607" crossorigin="anonymous"></script>
<div class="adsense-wrapper">
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="6970817239"></ins>
</div>
<script>
  (adsbygoogle = window.adsbygoogle || []).push({});
</script>



<h2>Conclusion</h2>



<p><strong>Flutter Driver</strong> a marqué une étape importante dans l’histoire du testing Flutter.<br>Il a permis aux développeurs d’aller au-delà des simples tests unitaires pour valider le comportement complet d’une application.</p>



<p>Même s’il cède peu à peu sa place à <code>integration_test</code>, il reste un <strong>outil robuste, pédagogique et efficace</strong> pour apprendre à automatiser des tests d’intégration dans Flutter.</p>



<p>En résumé : Flutter Driver est le chaînon entre le code et l’expérience utilisateur, un allié précieux pour toute équipe soucieuse de qualité et de fiabilité</p>



<div class="adsense-wrapper">
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="9812796514"></ins></div>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>
<p>The post <a href="https://flutter-now.com/tests-et-qualite/flutter-driver-le-guide-complet-pour-automatiser-vos-tests-dintegration-flutter/">Flutter Driver : Le guide complet pour automatiser vos tests d’intégration Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/tests-et-qualite/flutter-driver-le-guide-complet-pour-automatiser-vos-tests-dintegration-flutter/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">254</post-id>	</item>
		<item>
		<title>Fastlane, c&#8217;est quoi ?</title>
		<link>https://flutter-now.com/fastlane/fastlane-cest-quoi/</link>
					<comments>https://flutter-now.com/fastlane/fastlane-cest-quoi/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Tue, 14 Oct 2025 13:54:04 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Fastlane]]></category>
		<category><![CDATA[ios]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[Automatisation]]></category>
		<category><![CDATA[Déploiement]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Google Play Store]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=242</guid>

					<description><![CDATA[<p>Dans le monde du développement mobile, le déploiement d’une application sur l’App Store ou le Play Store est souvent une étape longue et fastidieuse. Entre [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/fastlane/fastlane-cest-quoi/">Fastlane, c&rsquo;est quoi ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Dans le monde du développement mobile, <strong>le déploiement d’une application</strong> sur l’App Store ou le Play Store est souvent une étape longue et fastidieuse. Entre la génération des certificats, la signature du code, la création des captures d’écran et la mise en ligne des nouvelles versions, les erreurs humaines sont fréquentes.<br>C’est là qu’intervient <strong>Fastlane</strong>.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-fastlane-en-quelques-mots">Fastlane en quelques mots</h2>



<p><strong>Fastlane</strong> est un <strong>outil open source</strong> (créé à l’origine par Felix Krause, aujourd’hui maintenu par Google) qui permet <strong>d’automatiser</strong> l’ensemble du processus de livraison et de publication d’applications mobiles.<br>Il est compatible avec <strong>iOS</strong>, <strong>Android</strong>, et même des projets <strong>React Native</strong> ou <strong>Flutter</strong>.</p>



<p>Son but est simple : <strong>gagner du temps</strong> et <strong>réduire les risques d’erreur</strong> lors des déploiements</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-que-peut-faire-fastlane">Que peut faire Fastlane ?</h2>



<p>Fastlane repose sur un ensemble de “<strong>lanes</strong>” (ou pistes), définies dans un fichier <strong>Fastfile</strong>, qui décrivent les actions à exécuter. </p>



<p>Voici quelques exemples de ce qu’il peut automatiser :</p>



<ul class="wp-block-list">
<li><strong>Génération et gestion automatique des certificats</strong> (avec <em>match</em>).</li>



<li><strong>Création de captures d’écran</strong> pour plusieurs appareils et langues (<em>snapshot</em>).</li>



<li><strong>Exécution des tests automatisés</strong> avant la livraison.</li>



<li><strong>Compilation et signature</strong> du binaire.</li>



<li><strong>Publication automatique</strong> sur TestFlight, App Store ou Google Play.</li>



<li><strong>Envoi des métadonnées</strong> (descriptions, mots-clés, captures, notes de version, etc.) vers les stores</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<!--<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1439580806237607"
     crossorigin="anonymous"></script>
<ins class="adsbygoogle"
     style="display:block; text-align:center;"
     data-ad-layout="in-article"
     data-ad-format="fluid"
     data-ad-client="ca-pub-1439580806237607"
     data-ad-slot="1661698549"></ins>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({});
</script>-->



<h2 class="wp-block-heading" id="h-exemple-concret-publier-son-app-et-ses-metadonnees-sur-l-app-store-ios">Exemple concret : publier son app et ses métadonnées sur l’App Store (iOS)</h2>



<p>Prenons un exemple concret avec une application iOS.<br>Tu veux envoyer une nouvelle version sur <strong>l’App Store</strong>, avec <strong>les métadonnées</strong> (titre, description, mots-clés) et <strong>les captures d’écran</strong> dans plusieurs langues.</p>



<p>Voici à quoi peut ressembler ton fichier <strong>FastFile</strong> :</p>



<pre class="wp-block-code"><code>default_platform(:ios)

platform :ios do
  desc "Publier une nouvelle version sur l'App Store avec métadonnées et screenshots"
  lane :release do
    # 1. Génération des certificats et profils de provisioning
    match(type: "appstore")

    # 2. Exécution des tests
    scan

    # 3. Création des captures d’écran multilingues
    snapshot

    # 4. Compilation et signature de l’app
    gym(scheme: "MyApp")

    # 5. Upload de l’app, des métadonnées et des screenshots
    deliver(
      force: true,
      skip_binary_upload: false,
      skip_screenshots: false,
      skip_metadata: false,
      submit_for_review: true,
      metadata_path: "./fastlane/metadata",        # &lt;-- répertoire contenant les métadonnées (par langue)
      screenshots_path: "./fastlane/screenshots"   # &lt;-- répertoire contenant les screenshots (par langue)
    )
  end
end
</code></pre>



<p>Ensuite, tu exécutes simplement</p>



<pre class="wp-block-code"><code>fastlane release</code></pre>



<p>Cette seule commande va :</p>



<ol class="wp-block-list">
<li>Gérer les certificats nécessaires.</li>



<li>Lancer les tests.</li>



<li>Générer toutes les captures d’écran (dans chaque langue et chaque appareil).</li>



<li>Compiler l’application.</li>



<li>Envoyer l’app, les <strong>métadonnées</strong> et les <strong>screenshots</strong> localisés directement sur l’App Store Connect.</li>



<li>Soumettre l’application pour validation.</li>
</ol>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Les métadonnées doivent être organisées dans <code>fastlane/metadata/&lt;langue&gt;/</code><br>Exemple:</p>
</blockquote>



<pre class="wp-block-code"><code>fastlane/metadata/fr-FR/description.txt  
fastlane/metadata/en-US/keywords.txt  
fastlane/metadata/fr-FR/marketing_url.txt  </code></pre>



<h2 class="wp-block-heading" id="h-exemple-concret-publier-sur-google-play-android">Exemple concret : publier sur Google Play (Android)</h2>



<p>Fastlane fonctionne tout aussi bien pour Android grâce à la commande<strong> supply</strong>, qui permet de gérer la publication sur le Google Play Store. </p>



<p>Voici un exemple de lane Android :</p>



<pre class="wp-block-code"><code>default_platform(:android)

platform :android do
  desc "Publier une nouvelle version sur Google Play avec métadonnées et screenshots"
  lane :release do
    # 1. Compilation du binaire (APK ou AAB)
    gradle(
      task: "bundle",
      build_type: "Release"
    )

    # 2. Envoi du binaire, métadonnées et captures sur le Play Store
    supply(
      aab: "app/build/outputs/bundle/release/app-release.aab",
      track: "production",
      json_key: "play-store-key.json",
      skip_upload_images: false,
      skip_upload_screenshots: false,
      skip_upload_metadata: false,
      metadata_path: "./fastlane/metadata/android",    # &lt;-- répertoire contenant les métadonnées Android
      screenshots_path: "./fastlane/metadata/android"  # &lt;-- répertoire contenant les captures par langue
    )
  end
end
</code></pre>



<p>Et tu exécutes simplement</p>



<pre class="wp-block-code"><code>fastlane release</code></pre>



<p>Fastlane va alors :</p>



<ol class="wp-block-list">
<li>Compiler ton application Android.</li>



<li>Charger automatiquement la version sur le <strong>Play Store</strong>.</li>



<li>Envoyer les <strong>métadonnées</strong>, les <strong>captures d’écran</strong> et les <strong>fichiers localisés</strong> pour chaque langue.</li>



<li>Publier la version sur la piste choisie (ex. <em>production</em>, <em>beta</em>, ou <em>internal</em>).</li>
</ol>



<p>our Android, les fichiers doivent être dans un dossier structuré ainsi :</p>



<pre class="wp-block-code"><code>fastlane/metadata/android/fr-FR/full_description.txt  
fastlane/metadata/android/en-US/short_description.txt  
fastlane/metadata/android/fr-FR/title.txt  
fastlane/metadata/android/fr-FR/images/phoneScreenshots/...</code></pre>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-pourquoi-adopter-fastlane">Pourquoi adopter Fastlane ?</h2>



<ul class="wp-block-list">
<li><strong>Gain de temps considérable</strong> sur les livraisons.</li>



<li><strong>Moins d’erreurs humaines</strong> grâce à l’automatisation.</li>



<li><strong>Homogénéité</strong> entre les environnements (staging, production, etc.).</li>



<li><strong>Intégration facile</strong> avec des outils CI/CD comme GitHub Actions, Bitrise ou Jenkins.</li>



<li><strong>Open source et gratuit</strong>.</li>



<li><strong>Indispensable pour les applications multilingues</strong> : Fastlane permet d’envoyer <strong>les métadonnées et les captures d’écran pour chaque langue</strong> automatiquement, garantissant une cohérence et un gain de temps énorme sur les projets internationaux.</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading" id="h-en-resume">En résumé</h2>



<p><strong>Fastlane</strong> est un outil incontournable pour automatiser la publication des applications mobiles, sur <strong>iOS comme Android</strong>.<br>Il permet de centraliser toutes les étapes du déploiement — du build à l’envoi sur le store — et de gérer facilement les <strong>métadonnées multilingues</strong> et <strong>captures d’écran localisées</strong>.<br>Un gain de temps, de fiabilité et de sérénité pour toutes les équipes de développement mobile.</p>
<p>The post <a href="https://flutter-now.com/fastlane/fastlane-cest-quoi/">Fastlane, c&rsquo;est quoi ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/fastlane/fastlane-cest-quoi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">242</post-id>	</item>
		<item>
		<title>Le Cycle de Vie d&#8217;une Application Flutter</title>
		<link>https://flutter-now.com/state/le-cycle-de-vie-dune-application-flutter/</link>
					<comments>https://flutter-now.com/state/le-cycle-de-vie-dune-application-flutter/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Tue, 11 Feb 2025 09:22:46 +0000</pubDate>
				<category><![CDATA[State]]></category>
		<category><![CDATA[Build]]></category>
		<category><![CDATA[CreateState]]></category>
		<category><![CDATA[Cycle de vie]]></category>
		<category><![CDATA[Deactivate]]></category>
		<category><![CDATA[Detached]]></category>
		<category><![CDATA[DidUpdateWidget]]></category>
		<category><![CDATA[dispose]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Inactive]]></category>
		<category><![CDATA[InitState]]></category>
		<category><![CDATA[Paused]]></category>
		<category><![CDATA[resumed]]></category>
		<category><![CDATA[SetState]]></category>
		<category><![CDATA[State Management]]></category>
		<category><![CDATA[StateFullWidget]]></category>
		<category><![CDATA[StateLessWidget]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=190</guid>

					<description><![CDATA[<p>Introduction Le cycle de vie d&#8217;une application Flutter est un concept essentiel pour les développeurs souhaitant gérer efficacement l&#8217;état de leur application. Comprendre ce cycle [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/state/le-cycle-de-vie-dune-application-flutter/">Le Cycle de Vie d&rsquo;une Application Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p>Le cycle de vie d&rsquo;une application Flutter est un concept essentiel pour les développeurs souhaitant gérer efficacement l&rsquo;état de leur application. Comprendre ce cycle permet de contrôler les ressources et de réagir aux changements d&rsquo;état tels que le passage en arrière-plan ou la fermeture de l&rsquo;application.</p>



<h2 class="wp-block-heading" id="h-etats-du-cycle-de-vie-d-une-application-flutter">États du cycle de vie d&rsquo;une application Flutter</h2>



<p>Une application Flutter passe par plusieurs étapes durant son exécution. Voici les principaux états :</p>



<ol start="1" class="wp-block-list">
<li><code><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">inactive</mark></strong></code> : L&rsquo;application est visible mais ne reçoit pas d&rsquo;interaction utilisateur.</li>



<li><code><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">resumed</mark></strong></code> : L&rsquo;application est au premier plan et interactive.</li>



<li><code><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">paused</mark></strong></code> : L&rsquo;application est en arrière-plan et ne reçoit pas d&rsquo;interaction.</li>



<li><code><strong><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">detached</mark></strong></code> : L&rsquo;application est sur le point d&rsquo;être supprimée de la mémoire.</li>
</ol>



<h2 class="wp-block-heading" id="h-schema-du-cycle-de-vie-de-l-application-flutter">Schéma du cycle de vie de l&rsquo;application Flutter</h2>



<p>Voici une représentation textuelle du cycle de vie d&rsquo;une application Flutter :</p>


<div class="wp-block-image is-style-default">
<figure class="aligncenter size-full"><img width="517" height="336"  alt="" class="wp-image-196 lws-optimize-lazyload"/ data-src="https://flutter-now.com/wp-content/uploads/2025/02/appState.png" srcset="https://flutter-now.com/wp-content/uploads/2025/02/appState.png 517w, https://flutter-now.com/wp-content/uploads/2025/02/appState-300x195.png 300w" sizes="(max-width: 517px) 100vw, 517px" /></figure>
</div>


<h2 class="wp-block-heading" id="h-cycle-de-vie-d-un-statefulwidget"><strong>Cycle de Vie d&rsquo;un StatefulWidget</strong></h2>



<p>Voici toutes les étapes détaillées du cycle de vie d&rsquo;un <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatefulWidget</mark></code> avec un schéma amélioré et des extraits de code illustrant chaque étape :</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img width="334" height="621"  alt="" class="wp-image-214 lws-optimize-lazyload"/ data-src="https://flutter-now.com/wp-content/uploads/2025/02/sateFullWidget.png" srcset="https://flutter-now.com/wp-content/uploads/2025/02/sateFullWidget.png 334w, https://flutter-now.com/wp-content/uploads/2025/02/sateFullWidget-161x300.png 161w" sizes="(max-width: 334px) 100vw, 334px" /></figure>
</div>


<p><strong>Exemple de Code StatefulWidget</strong></p>



<pre class="wp-block-code"><code>class MyStatefulWidget extends StatefulWidget {
  @override
  _MyStatefulWidgetState createState() =&gt; _MyStatefulWidgetState();
}

class _MyStatefulWidgetState extends State&lt;MyStatefulWidget&gt; {
  @override
  void initState() {
    super.initState();
    print("initState: Initialisation du widget");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    print("didChangeDependencies: Dépendances mises à jour");
  }

  @override
  void didUpdateWidget(covariant MyStatefulWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    print("didUpdateWidget: Widget mis à jour");
  }

  @override
  Widget build(BuildContext context) {
    print("build: Reconstruction du widget");
    return Scaffold(
      appBar: AppBar(title: Text("Cycle de Vie StatefulWidget")),
      body: Center(child: Text("Widget Stateful en action")),
    );
  }

  @override
  void deactivate() {
    print("deactivate: Widget retiré temporairement");
    super.deactivate();
  }

  @override
  void dispose() {
    print("dispose: Suppression définitive du widget");
    super.dispose();
  }
}</code></pre>



<h3 class="wp-block-heading">Explication détaillée des étapes</h3>



<ol start="1" class="wp-block-list">
<li><code><strong>createState()</strong></code> : Crée l&rsquo;état associé au widget. Exécuté une seule fois.</li>



<li><code><strong>initState()</strong></code> : Initialisation de l&rsquo;état du widget, appelé une seule fois au montage.</li>



<li><code><strong>didChangeDependencies()</strong></code> : Appelé lorsque les objets dépendants changent.</li>



<li><code><strong>build()</strong></code> : Construit l&rsquo;interface utilisateur et est appelé à chaque mise à jour.</li>



<li><code><strong>didUpdateWidget()</strong></code> : Appelé lorsque le widget parent est reconstruit et transmet de nouvelles propriétés.</li>



<li><code><strong>deactivate()</strong></code> : Appelé lorsque le widget est temporairement retiré de l&rsquo;arbre des widgets.</li>



<li><code><strong>dispose()</strong></code> : Nettoie les ressources avant la suppression définitive de l&rsquo;état du widget.</li>
</ol>



<h2 class="wp-block-heading">Cycle de Vie d&rsquo;un StatelessWidget</h2>



<p>Contrairement aux <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatefulWidget</mark></code>, un <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatelessWidget</mark></code> ne possède pas d&rsquo;état interne. Son cycle de vie est donc beaucoup plus simple et suit uniquement les étapes suivantes :</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img width="350" height="87"  alt="" class="wp-image-199 lws-optimize-lazyload"/ data-src="https://flutter-now.com/wp-content/uploads/2025/02/statelessWidget.png" srcset="https://flutter-now.com/wp-content/uploads/2025/02/statelessWidget.png 350w, https://flutter-now.com/wp-content/uploads/2025/02/statelessWidget-300x75.png 300w" sizes="(max-width: 350px) 100vw, 350px" /></figure>
</div>


<h3 class="wp-block-heading">1. <code>build()</code> &#8211; Construction de l&rsquo;interface utilisateur</h3>



<p>Dans un <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatelessWidget</mark></code>, la méthode <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">build()</mark></code> est la seule étape importante du cycle de vie. Elle est appelée lorsqu&rsquo;il est nécessaire de construire l&rsquo;interface utilisateur du widget.</p>



<pre class="wp-block-code"><code>class MyStatelessWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("build: Construction du widget Stateless");
    return Scaffold(
      appBar: AppBar(title: Text("Cycle de Vie StatelessWidget")),
      body: Center(child: Text("Widget Stateless en action")),
    );
  }
}</code></pre>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Comprendre et gérer le cycle de vie d&rsquo;une application Flutter permet d&rsquo;améliorer la gestion des ressources et d&rsquo;offrir une meilleure expérience utilisateur. En utilisant <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">WidgetsBindingObserver</mark></code>, les développeurs peuvent surveiller et réagir aux transitions de l&rsquo;application de manière efficace. De plus, la gestion du cycle de vie des <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatefulWidget</mark></code> et <code><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-vivid-green-cyan-color">StatelessWidget</mark></code> est cruciale pour assurer un comportement optimal des composants dynamiques et statiques de l&rsquo;application.</p>



<p></p>
<p>The post <a href="https://flutter-now.com/state/le-cycle-de-vie-dune-application-flutter/">Le Cycle de Vie d&rsquo;une Application Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/state/le-cycle-de-vie-dune-application-flutter/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">190</post-id>	</item>
		<item>
		<title>Supabase : Une Alternative Open-Source à Firebase</title>
		<link>https://flutter-now.com/backend/supabase-une-alternative-open-source-a-firebase/</link>
					<comments>https://flutter-now.com/backend/supabase-une-alternative-open-source-a-firebase/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Thu, 30 Jan 2025 16:25:22 +0000</pubDate>
				<category><![CDATA[Backend]]></category>
		<category><![CDATA[Firebase]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Serveur]]></category>
		<category><![CDATA[Supabase]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=177</guid>

					<description><![CDATA[<p>Supabase est une plateforme open-source qui fournit des fonctionnalités Backend-as-a-Service (BaaS), similaire à Firebase. Il repose sur PostgreSQL et offre une gamme de services tels [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/backend/supabase-une-alternative-open-source-a-firebase/">Supabase : Une Alternative Open-Source à Firebase</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Supabase est une plateforme open-source qui fournit des fonctionnalités Backend-as-a-Service (BaaS), similaire à Firebase. Il repose sur <strong>PostgreSQL</strong> et offre une gamme de services tels que l&rsquo;authentification, la gestion des bases de données en temps réel, le stockage de fichiers et les fonctions serverless. Supabase est conçu pour être une alternative open-source robuste à Firebase, avec des fonctionnalités avancées et une plus grande flexibilité.</p>



<h2 class="wp-block-heading" id="h-1-fonctionnalites-de-supabase"><strong>1. Fonctionnalités de Supabase</strong></h2>



<p>Supabase propose plusieurs fonctionnalités essentielles pour le développement d&rsquo;applications modernes :</p>



<h3 class="wp-block-heading" id="h-a-base-de-donnees-postgresql"><strong>a) Base de Données PostgreSQL</strong></h3>



<p>Supabase repose sur PostgreSQL, une base de données relationnelle puissante. Elle offre des fonctionnalités avancées comme :</p>



<ul class="wp-block-list">
<li>Les requêtes SQL avancées</li>



<li>Les relations entre tables</li>



<li>Le support des fonctions stockées et des triggers</li>
</ul>



<h3 class="wp-block-heading" id="h-b-authentification-et-gestion-des-utilisateurs"><strong>b) Authentification et Gestion des Utilisateurs</strong></h3>



<p>Supabase propose un système d&rsquo;authentification robuste qui supporte :</p>



<ul class="wp-block-list">
<li>Connexion par email/mot de passe</li>



<li>Authentification sociale (Google, GitHub, Twitter, etc.)</li>



<li>Authentification via OAuth et JWT</li>



<li>Règles de sécurité avancées pour contrôler l&rsquo;accès aux ressources</li>
</ul>



<h3 class="wp-block-heading" id="h-c-stockage-de-fichiers"><strong>c) Stockage de Fichiers</strong></h3>



<p>Supabase inclut un système de stockage permettant d&rsquo;héberger des fichiers tels que des images et des vidéos. Il utilise des permissions configurables et une API pour gérer les fichiers.</p>



<h3 class="wp-block-heading" id="h-d-realtime-database"><strong>d) Realtime Database</strong></h3>



<p>Grâce à <strong>Realtime Postgres</strong>, Supabase permet de recevoir des mises à jour en temps réel sur les changements de la base de données, ce qui est idéal pour les applications collaboratives ou de chat.</p>



<h3 class="wp-block-heading" id="h-e-api-automatique"><strong>e) API Automatique</strong></h3>



<p>À partir d&rsquo;une base de données PostgreSQL, Supabase génère automatiquement une API RESTful, facilitant l&rsquo;accès aux données depuis une application front-end.</p>



<h3 class="wp-block-heading" id="h-f-fonctions-serverless-edge-functions"><strong>f) Fonctions Serverless (Edge Functions)</strong></h3>



<p>Supabase permet d&rsquo;exécuter des <strong>fonctions serverless</strong>, ce qui permet d&rsquo;ajouter de la logique back-end sans gérer d&rsquo;infrastructure complexe.</p>



<h2 class="wp-block-heading" id="h-2-installation-et-configuration-de-supabase"><strong>2. Installation et Configuration de Supabase</strong></h2>



<h3 class="wp-block-heading" id="h-a-creation-d-un-projet-supabase"><strong>a) Création d&rsquo;un projet Supabase</strong></h3>



<ol class="wp-block-list">
<li>Rendez-vous sur<a href="https://supabase.com/"> Supabase</a> et créez un compte.</li>



<li>Créez un nouveau projet et notez l&rsquo;URL et la clé API fournie par Supabase.</li>



<li>Accédez à la console de gestion pour configurer votre base de données et vos services.</li>
</ol>



<h3 class="wp-block-heading" id="h-b-installation-du-sdk-supabase"><strong>b) Installation du SDK Supabase</strong></h3>



<p>Supabase fournit un SDK pour interagir avec ses services en utilisant JavaScript, Flutter, Python, etc.</p>



<p>Si vous utilisez <strong>Node.js</strong>, installez le SDK via NPM :</p>



<pre class="wp-block-code"><code>npm install @supabase/supabase-js</code></pre>



<p>Si vous utilisez <strong>Flutter</strong>, installez le package via pub.dev :</p>



<pre class="wp-block-code"><code>dependencies:
  supabase_flutter: ^1.2.0</code></pre>



<h2 class="wp-block-heading"><strong>3. Intégration de Supabase avec Flutter</strong></h2>



<h3 class="wp-block-heading"><strong>a) Initialisation de Supabase dans Flutter</strong></h3>



<p>Ajoutez la configuration de Supabase dans votre projet Flutter.</p>



<p>Dans main.dart :</p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Supabase.initialize(
    url: 'https://your-project-url.supabase.co',
    anonKey: 'your-anon-key',
  );
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}</code></pre>



<h3 class="wp-block-heading"><strong>b) Authentification avec Supabase</strong></h3>



<p>Implémenter l&rsquo;inscription d&rsquo;un utilisateur par email/mot de passe :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; signUpUser(String email, String password) async {
  final response = await Supabase.instance.client.auth.signUp(
    email: email,
    password: password,
  );
  print('Utilisateur inscrit : ${response.user?.email}');
}</code></pre>



<p>Implémenter une connexion par email/mot de passe :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; signInUser(String email, String password) async {
  final response = await Supabase.instance.client.auth.signInWithPassword(
    email: email,
    password: password,
  );
  print('Utilisateur connecté : ${response.user?.email}');
}
</code></pre>



<p>Déconnexion de l’utilisateur :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; signOutUser() async {
  await Supabase.instance.client.auth.signOut();
}</code></pre>



<h3 class="wp-block-heading"><strong>c) Gestion des Données avec Supabase</strong></h3>



<p>Insertion de données :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; addUser(String name, String email) async {
  await Supabase.instance.client.from('users').insert({
    'name': name,
    'email': email,
  });
}</code></pre>



<p>Lecture de données :</p>



<pre class="wp-block-code"><code>Future&lt;List&lt;Map&lt;String, dynamic&gt;&gt;&gt; getUsers() async {
  final response = await Supabase.instance.client.from('users').select();
  return response;
}</code></pre>



<p>Mise à jour d&rsquo;un utilisateur :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; updateUser(int id, String newName) async {
  await Supabase.instance.client.from('users').update({
    'name': newName,
  }).match({'id': id});
}</code></pre>



<p>Suppression d&rsquo;un utilisateur :</p>



<pre class="wp-block-code"><code>Future&lt;void&gt; deleteUser(int id) async {
  await Supabase.instance.client.from('users').delete().match({'id': id});
}</code></pre>



<h3 class="wp-block-heading"><strong>d) Gestion des Événements en Temps Réel</strong></h3>



<p>Insertion de données :</p>



<p>Abonnement aux mises à jour de la base de données en temps réel :</p>



<pre class="wp-block-code"><code>void listenToUsers() {
  Supabase.instance.client
    .from('users')
    .stream(primaryKey: &#91;'id'])
    .listen((data) {
      print('Données mises à jour : $data');
    });
}</code></pre>



<h2 class="wp-block-heading" id="h-4-prix-de-supabase"><strong><strong>4. Prix de Supabase</strong></strong></h2>



<p>Supabase propose un modèle de tarification flexible comprenant plusieurs plans :</p>



<ul class="wp-block-list">
<li><strong>Gratuit</strong> : Idéal pour les développeurs individuels et les petites applications. Il inclut :
<ul class="wp-block-list">
<li>500 Mo de stockage</li>



<li>50 000 requêtes par mois</li>



<li>2 bases de données avec 500 000 lignes chacune</li>



<li>1 Go de bande passante pour l&rsquo;authentification et le stockage</li>



<li>Fonctionnalités essentielles de Supabase, y compris l&rsquo;authentification, le stockage et les mises à jour en temps réel.</li>
</ul>
</li>



<li><strong>Pro</strong> : À partir de 25$/mois, destiné aux startups et projets en croissance. Il offre :
<ul class="wp-block-list">
<li>8 Go de stockage</li>



<li>5 millions de requêtes par mois</li>



<li>Bases de données évolutives</li>



<li>50 Go de bande passante</li>



<li>Accès à des fonctionnalités avancées et un support prioritaire</li>
</ul>
</li>



<li><strong>Entreprise</strong> : Offre personnalisée pour les grandes entreprises, avec :
<ul class="wp-block-list">
<li>Des performances évolutives</li>



<li>Un support dédié 24/7</li>



<li>Sécurité et conformité avancées (SOC2, HIPAA, etc.)</li>



<li>Options de déploiement sur mesure, y compris auto-hébergement</li>
</ul>
</li>
</ul>



<p>Les prix peuvent varier, et il est recommandé de consulter le site officiel pour obtenir les dernières informations.</p>



<h2 class="wp-block-heading"><strong>Conclusion</strong></h2>



<p>Supabase est une excellente alternative open-source à Firebase, offrant des fonctionnalités puissantes basées sur PostgreSQL. Son intégration avec Flutter est simple et permet d’ajouter rapidement des fonctionnalités backend à votre application. Grâce à Supabase, les développeurs peuvent gérer facilement l&rsquo;authentification, la base de données, le stockage et les mises à jour en temps réel tout en ayant un contrôle total sur leur infrastructure.</p>



<h2 class="wp-block-heading" id="h-git-sample"><strong>Git Sample</strong></h2>



<figure class="wp-block-table"><table><tbody><tr><td><strong>Supabase Auth sample</strong></td><td>
<script async="" defer="" src="https://buttons.github.io/buttons.js"></script>
<center>
<a class="github-button" href="https://github.com/geof34730/flutternow-supabase-auth" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" aria-label="Star geof34730/flutternow-supabase-auth on GitHub">Star</a>
<a class="github-button" href="https://github.com/geof34730/flutternow-supabase-auth/archive/HEAD.zip" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" aria-label="Download geof34730/flutternow-supabase-auth on GitHub">Download</a></center>
</td></tr></tbody></table></figure>



<p></p>
<p>The post <a href="https://flutter-now.com/backend/supabase-une-alternative-open-source-a-firebase/">Supabase : Une Alternative Open-Source à Firebase</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/backend/supabase-une-alternative-open-source-a-firebase/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">177</post-id>	</item>
		<item>
		<title>Gestion d&#8217;état avec StreamController et persistance locale dans Flutter</title>
		<link>https://flutter-now.com/state/gestion-detat-avec-streamcontroller-et-persistance-locale-dans-flutter/</link>
					<comments>https://flutter-now.com/state/gestion-detat-avec-streamcontroller-et-persistance-locale-dans-flutter/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Wed, 29 Jan 2025 16:03:46 +0000</pubDate>
				<category><![CDATA[State]]></category>
		<category><![CDATA[Bloc]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[LocalStorage]]></category>
		<category><![CDATA[State Management]]></category>
		<category><![CDATA[StreamController]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=166</guid>

					<description><![CDATA[<p>Introduction Flutter est un framework populaire pour le développement d&#8217;applications mobiles multiplateformes. L&#8217;un des défis majeurs lors du développement d&#8217;une application est la gestion de [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/state/gestion-detat-avec-streamcontroller-et-persistance-locale-dans-flutter/">Gestion d&rsquo;état avec StreamController et persistance locale dans Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p>Flutter est un framework populaire pour le développement d&rsquo;applications mobiles multiplateformes. L&rsquo;un des défis majeurs lors du développement d&rsquo;une application est la gestion de l&rsquo;état. L&rsquo;utilisation d&rsquo;un <code><strong>StreamController</strong></code> et du <code><strong>Bloc Pattern</strong></code> est une approche efficace pour gérer les flux de données en temps réel.</p>



<p>Ce tutoriel explique comment implémenter un <code><strong>Bloc</strong></code> en utilisant un <code><strong>StreamController</strong></code> pour gérer une application simple de gestion de tâches (Todo List), incluant la persistance des données avec <code><strong>localstorage</strong></code>.</p>



<h2 class="wp-block-heading" id="h-prerequis">Prérequis</h2>



<p>Avant de commencer, assurez-vous d&rsquo;avoir :</p>



<ul class="wp-block-list">
<li>Flutter installé sur votre machine</li>



<li>Un éditeur comme Visual Studio Code ou Android Studio</li>



<li>Une connaissance de base en Flutter et Dart</li>
</ul>



<h2 class="wp-block-heading" id="h-bloc-avec-streamcontroller-vs-flutter-bloc">Bloc avec StreamController vs flutter_bloc</h2>



<p>Dans ce tutoriel, nous utilisons directement <code><strong>StreamController</strong></code> pour gérer l&rsquo;état, ce qui est une approche plus légère et simple. Cependant, Flutter propose également une bibliothèque nommée <code><strong>flutter_bloc</strong></code> qui suit un modèle basé sur les événements et états.</p>



<p><strong>Différences principales :</strong></p>



<figure class="wp-block-table"><table><thead><tr><th>StreamController</th><th>flutter_bloc</th></tr></thead><tbody><tr><td>Plus simple et léger</td><td>Plus structuré et robuste</td></tr><tr><td>Utilise directement des <code><strong>StreamController</strong></code></td><td>Utilise une architecture basée sur des événements et des états</td></tr><tr><td>Moins de code boilerplate</td><td>Nécessite plus de configuration</td></tr><tr><td>Idéal pour les petites applications</td><td>Idéal pour les applications complexes</td></tr></tbody></table></figure>



<p>Si vous souhaitez une gestion d&rsquo;état plus avancée avec une meilleure séparation des responsabilités, <code><strong>flutter_bloc</strong></code> est une bonne alternative. Sinon, <code><strong>StreamController</strong></code> est suffisant pour des applications plus légères.</p>



<h2 class="wp-block-heading">Installation des dépendances</h2>



<p>Ajoutez les dépendances <code><strong>localstorage</strong></code> dans le fichier <code><strong>pubspec.yaml</strong></code> :</p>



<pre class="wp-block-code"><code>dependencies:
  flutter:
    sdk: flutter
  localstorage: ^4.0.0</code></pre>



<p>Puis exécutez la commande :</p>



<pre class="wp-block-code"><code>flutter pub get</code></pre>



<h2 class="wp-block-heading">Implémentation du Bloc avec StreamController</h2>



<h3 class="wp-block-heading">1. Créer le modèle de données</h3>



<p>Créons un fichier <code><strong>todo_model.dart</strong></code> pour définir notre modèle de tâche :</p>



<pre class="wp-block-code"><code>class TodoModel {
  final String task;
  final bool isCompleted;

  TodoModel({required this.task, this.isCompleted = false});

  // Convert object to JSON
  Map&lt;String, dynamic&gt; toJson() =&gt; {
        'task': task,
        'isCompleted': isCompleted,
      };

  // Convert JSON to object
  factory TodoModel.fromJson(Map&lt;String, dynamic&gt; json) =&gt; TodoModel(
        task: json&#91;'task'],
        isCompleted: json&#91;'isCompleted'],
      );
}</code></pre>



<h3 class="wp-block-heading">2. Créer le Bloc avec StreamController</h3>



<p>Créons un fichier <code><strong>todo_bloc.dart</strong></code> :</p>



<pre class="wp-block-code"><code>import 'dart:async';
import 'package:localstorage/localstorage.dart';
import 'todo_model.dart';

class TodoBloc {
  final LocalStorage storage = LocalStorage('todo_app');
  List&lt;TodoModel&gt; _todos = &#91;];

  // StreamController for managing the todo list state
  final StreamController&lt;List&lt;TodoModel&gt;&gt; _streamController = StreamController&lt;List&lt;TodoModel&gt;&gt;.broadcast();

  // Sink for adding new states
  Sink&lt;List&lt;TodoModel&gt;&gt; get sink =&gt; _streamController.sink;

  // Stream for listening to updates
  Stream&lt;List&lt;TodoModel&gt;&gt; get stream =&gt; _streamController.stream;

  // Load saved todos from local storage
  Future&lt;void&gt; loadTodos() async {
    await storage.ready;
    List&lt;dynamic&gt;? storedTodos = storage.getItem('todos');
    _todos = storedTodos?.map((e) =&gt; TodoModel.fromJson(e)).toList() ?? &#91;];
    sink.add(_todos);
  }

  // Add a new todo
  void addTodo(String task) {
    _todos.add(TodoModel(task: task));
    storage.setItem('todos', _todos.map((e) =&gt; e.toJson()).toList());
    sink.add(_todos);
  }

  // Remove a todo by index
  void removeTodo(int index) {
    _todos.removeAt(index);
    storage.setItem('todos', _todos.map((e) =&gt; e.toJson()).toList());
    sink.add(_todos);
  }

  // Dispose method to close the StreamController
  void dispose() {
    _streamController.close();
  }
}

// Create a singleton instance of the bloc
final todoBloc = TodoBloc();</code></pre>



<h3 class="wp-block-heading">3. Intégrer le Bloc dans l&rsquo;UI</h3>



<p>Dans le fichier <code><strong>main.dart</strong></code>, utilisez un <code><strong>StreamBuilder</strong></code> pour écouter les mises à jour :</p>



<pre class="wp-block-code"><code>import 'package:flutter/material.dart';
import 'todo_bloc.dart';
import 'todo_model.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TodoScreen(),
    );
  }
}

class TodoScreen extends StatefulWidget {
  @override
  _TodoScreenState createState() =&gt; _TodoScreenState();
}

class _TodoScreenState extends State&lt;TodoScreen&gt; {
  final TextEditingController _controller = TextEditingController();

  @override
  void initState() {
    super.initState();
    todoBloc.loadTodos();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Todo List')),
      body: Column(
        children: &#91;
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: TextField(
              controller: _controller,
              decoration: InputDecoration(labelText: 'Enter task'),
            ),
          ),
          ElevatedButton(
            onPressed: () {
              if (_controller.text.isNotEmpty) {
                todoBloc.addTodo(_controller.text);
                _controller.clear();
              }
            },
            child: Text('Add Todo'),
          ),
          Expanded(
            child: StreamBuilder&lt;List&lt;TodoModel&gt;&gt;(
              stream: todoBloc.stream,
              builder: (context, snapshot) {
                if (!snapshot.hasData || snapshot.data!.isEmpty) {
                  return Center(child: Text('No tasks available'));
                }
                return ListView.builder(
                  itemCount: snapshot.data!.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text(snapshot.data!&#91;index].task),
                      trailing: IconButton(
                        icon: Icon(Icons.delete),
                        onPressed: () =&gt; todoBloc.removeTodo(index),
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}</code></pre>




<script async defer src="https://buttons.github.io/buttons.js"></script>
<center>
<a class="github-button" href="https://github.com/geof34730/FlutterNow-bloc-with-streamController" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" aria-label="Star geof34730/FlutterNow-bloc-with-streamController on GitHub">Star</a>
<a class="github-button" href="https://github.com/geof34730/FlutterNow-bloc-with-streamController/archive/HEAD.zip" data-color-scheme="no-preference: dark; light: dark; dark: dark;" data-size="large" aria-label="Download geof34730/FlutterNow-bloc-with-streamController on GitHub">Download</a></center>



<h2 class="wp-block-heading">Conclusion</h2>



<p>En suivant ce tutoriel, vous avez appris à :</p>



<ul class="wp-block-list">
<li>Gérer l&rsquo;état d&rsquo;une liste de tâches avec un <code><strong>StreamController</strong></code></li>



<li>Ajouter la persistance des données avec <code><strong>localstorage</strong></code></li>



<li>Utiliser un <code><strong>StreamBuilder</strong></code> pour gérer l&rsquo;affichage dynamique</li>



<li>Comprendre la différence entre <code><strong>StreamController</strong></code> et <code><strong>flutter_bloc</strong></code></li>
</ul>



<p>Cette approche est efficace pour des applications nécessitant des mises à jour en temps réel. Vous pouvez améliorer cette architecture en ajoutant la gestion des tâches complétées ou en intégrant <code><strong>flutter_bloc</strong></code> pour un projet plus complexe.</p>
<p>The post <a href="https://flutter-now.com/state/gestion-detat-avec-streamcontroller-et-persistance-locale-dans-flutter/">Gestion d&rsquo;état avec StreamController et persistance locale dans Flutter</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/state/gestion-detat-avec-streamcontroller-et-persistance-locale-dans-flutter/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">166</post-id>	</item>
		<item>
		<title>Introduction à la gestion d’état dans Flutter : Provider, Riverpod ou Bloc ?</title>
		<link>https://flutter-now.com/state/introduction-a-la-gestion-detat-dans-flutter-provider-riverpod-ou-bloc/</link>
					<comments>https://flutter-now.com/state/introduction-a-la-gestion-detat-dans-flutter-provider-riverpod-ou-bloc/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Wed, 15 Jan 2025 10:11:12 +0000</pubDate>
				<category><![CDATA[State]]></category>
		<category><![CDATA[Bloc]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Provider]]></category>
		<category><![CDATA[Riverpod]]></category>
		<category><![CDATA[State Management]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=116</guid>

					<description><![CDATA[<p>La gestion d’état est un sujet central pour tout développeur Flutter souhaitant créer des applications complexes. Dans un framework basé sur les widgets comme Flutter, [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/state/introduction-a-la-gestion-detat-dans-flutter-provider-riverpod-ou-bloc/">Introduction à la gestion d’état dans Flutter : Provider, Riverpod ou Bloc ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>La gestion d’état est un sujet central pour tout développeur Flutter souhaitant créer des applications complexes. Dans un framework basé sur les widgets comme Flutter, il est crucial de déterminer comment partager et synchroniser des données entre différentes parties de l’application. Cet article explore trois des solutions les plus populaires : <strong>Provider</strong>, <strong>Riverpod</strong>, et <strong>Bloc</strong>.</p>



<h2 class="wp-block-heading" id="h-pourquoi-la-gestion-d-etat-est-elle-importante">Pourquoi la gestion d’état est-elle importante ?</h2>



<p>Une application typique implique des interactions complexes : formulaires, appels API, stockage local, etc. Ces interactions nécessitent de gérer des états comme :</p>



<ul class="wp-block-list">
<li>Les données utilisateurs (profil, préférences, etc.).</li>



<li>Les états de chargement et d’erreur.</li>



<li>La navigation conditionnelle.</li>
</ul>



<p>Sans une stratégie claire, la gestion de ces états peut rapidement devenir chaotique. Flutter, à travers sa philosophie de développement réactif, offre plusieurs outils pour maintenir un code propre et maintenable</p>



<h2 class="wp-block-heading">Comparaison des solutions de gestion d’état</h2>



<h3 class="wp-block-heading"><strong>1. Provider</strong></h3>



<p>Provider est souvent considéré comme la solution “officielle”, car il a été créé par Remi Rousselet, l’un des contributeurs clés de Flutter. Il repose sur le système d’injection de dépendances et la notification des écouteurs.</p>



<h4 class="wp-block-heading">Avantages :</h4>



<ul class="wp-block-list">
<li><strong>Facilité d’utilisation :</strong> Une API simple pour les cas d’utilisation courants.</li>



<li><strong>Flexibilité :</strong> Convient à la plupart des projets, petits ou grands.</li>



<li><strong>Performances :</strong> Optimisé pour minimiser les reconstructions inutiles.</li>
</ul>



<h4 class="wp-block-heading">Inconvénients :</h4>



<ul class="wp-block-list">
<li><strong>Configuration :</strong> Peut nécessiter un peu de code boilerplate pour des cas avancés.</li>



<li><strong>Manque de modernité :</strong> Les nouvelles solutions comme Riverpod offrent des fonctionnalités plus modernes.</li>
</ul>



<p><strong>Exemple </strong>:</p>



<pre class="wp-block-code"><code>class Counter with ChangeNotifier {
  int _count = 0;
  int get count =&gt; _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) =&gt; Counter(),
      child: MyApp(),
    ),
  );
}</code></pre>



<h3 class="wp-block-heading"><strong>2. Riverpod</strong></h3>



<p>Riverpod est une évolution de Provider, également créé par Remi Rousselet. Il offre une syntaxe plus claire, une meilleure gestion des erreurs et ne repose pas sur le contexte de Flutter, ce qui le rend plus flexible.</p>



<h4 class="wp-block-heading">Avantages :</h4>



<ul class="wp-block-list">
<li><strong>Indépendant du contexte :</strong> Les providers peuvent être utilisés partout, même en dehors de l’arborescence des widgets.</li>



<li><strong>API moderne :</strong> Plus intuitive et puissante.</li>



<li><strong>Support des tests :</strong> Simplifie énormément les tests unitaires.</li>
</ul>



<h4 class="wp-block-heading">Inconvénients :</h4>



<ul class="wp-block-list">
<li><strong>Nouvelle courbe d’apprentissage :</strong> Les développeurs habitués à Provider doivent s’adapter.</li>
</ul>



<h4 class="wp-block-heading">Exemple :</h4>



<pre class="wp-block-code"><code>final counterProvider = StateProvider&lt;int&gt;((ref) =&gt; 0);

class CounterApp extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Scaffold(
      appBar: AppBar(title: Text('Counter App')),
      body: Center(child: Text('Count: $count')),
      floatingActionButton: FloatingActionButton(
        onPressed: () =&gt; ref.read(counterProvider.notifier).state++,
        child: Icon(Icons.add),
      ),
    );
  }
}</code></pre>



<h3 class="wp-block-heading"><strong>3. Bloc (Business Logic Component)</strong></h3>



<p>Bloc est une bibliothèque basée sur le modèle de gestion d’état à l’aide de streams et d’événements. Elle s’inspire des architectures réactives et s’adapte bien aux grandes applications.</p>



<h4 class="wp-block-heading">Avantages :</h4>



<ul class="wp-block-list">
<li><strong>Structure claire :</strong> Imposant une séparation stricte entre l’interface utilisateur et la logique.</li>



<li><strong>Bonne documentation :</strong> Large communauté et nombreux exemples.</li>



<li><strong>Adapté aux grandes applications :</strong> Facilite la collaboration et la maintenance.</li>
</ul>



<h4 class="wp-block-heading">Inconvénients :</h4>



<ul class="wp-block-list">
<li><strong>Complexité :</strong> Courbe d’apprentissage plus raide, surtout pour les débutants.</li>



<li><strong>Code verbeux :</strong> Peut sembler lourd pour des projets simples.</li>
</ul>



<h4 class="wp-block-heading">Exemple :</h4>



<pre class="wp-block-code"><code>class CounterCubit extends Cubit&lt;int&gt; {
  CounterCubit() : super(0);

  void increment() =&gt; emit(state + 1);
}

void main() {
  runApp(
    BlocProvider(
      create: (_) =&gt; CounterCubit(),
      child: MyApp(),
    ),
  );
}</code></pre>



<p><strong>Comparatif rapide</strong></p>



<figure class="wp-block-table"><table><thead><tr><th>Critère</th><th>Provider</th><th>Riverpod</th><th>Bloc</th></tr></thead><tbody><tr><td><strong>Facilité d’utilisation</strong></td><td>Simple</td><td>Intuitif</td><td>Complexe</td></tr><tr><td><strong>Performances</strong></td><td>Bonne</td><td>Excellente</td><td>Bonne</td></tr><tr><td><strong>Flexibilité</strong></td><td>Moyenne</td><td>Très élevée</td><td>Élevée</td></tr><tr><td><strong>Courbe d’apprentissage</strong></td><td>Faible</td><td>Moyenne</td><td>Haute</td></tr><tr><td><strong>Adapté aux tests</strong></td><td>Moyen</td><td>Excellent</td><td>Excellent</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">Conclusion</h2>



<p>Le choix entre Provider, Riverpod et Bloc dépend de vos besoins spécifiques :</p>



<ul class="wp-block-list">
<li><strong>Provider</strong> : Idéal pour les petits projets ou pour les développeurs qui débutent avec Flutter.</li>



<li><strong>Riverpod</strong> : Une solution moderne, flexible et puissante, parfaite pour les projets de toutes tailles.</li>



<li><strong>Bloc</strong> : Adapté aux grandes applications avec des exigences complexes et une équipe de développeurs.</li>
</ul>



<p>Explorez ces solutions et choisissez celle qui convient le mieux à vos projets. Quelle que soit votre option, une bonne gestion d’état est la clé d’une application Flutter réussie et maintenable !</p>
<p>The post <a href="https://flutter-now.com/state/introduction-a-la-gestion-detat-dans-flutter-provider-riverpod-ou-bloc/">Introduction à la gestion d’état dans Flutter : Provider, Riverpod ou Bloc ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/state/introduction-a-la-gestion-detat-dans-flutter-provider-riverpod-ou-bloc/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">116</post-id>	</item>
		<item>
		<title>Flutter : un avenir incertain face à la baisse d&#8217;investissement de Google ?</title>
		<link>https://flutter-now.com/actualites/flutter-un-avenir-incertain-face-a-la-baisse-dinvestissement-de-google/</link>
					<comments>https://flutter-now.com/actualites/flutter-un-avenir-incertain-face-a-la-baisse-dinvestissement-de-google/#respond</comments>
		
		<dc:creator><![CDATA[Geoffrey]]></dc:creator>
		<pubDate>Tue, 14 Jan 2025 08:36:58 +0000</pubDate>
				<category><![CDATA[Actualités]]></category>
		<category><![CDATA[Avenir]]></category>
		<category><![CDATA[Flutter]]></category>
		<category><![CDATA[Google]]></category>
		<guid isPermaLink="false">https://flutter-now.com/?p=92</guid>

					<description><![CDATA[<p>Depuis son lancement en 2017, Flutter, le framework open source de Google pour le développement d’applications multiplateformes, a révolutionné le paysage du développement logiciel. Sa [&#8230;]</p>
<p>The post <a href="https://flutter-now.com/actualites/flutter-un-avenir-incertain-face-a-la-baisse-dinvestissement-de-google/">Flutter : un avenir incertain face à la baisse d&rsquo;investissement de Google ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Depuis son lancement en 2017, Flutter, le framework open source de Google pour le développement d’applications multiplateformes, a révolutionné le paysage du développement logiciel. Sa promesse de créer des applications fonctionnant sur Android, iOS, le web et le desktop à partir d’un seul code source a attiré des millions de développeurs et entreprises. Pourtant, des événements récents suscitent des interrogations quant à son avenir.</p>



<h3 class="wp-block-heading" id="h-des-signes-d-une-baisse-d-investissement"><strong>Des signes d’une baisse d’investissement</strong></h3>



<p>En mai 2024, Google a effectué des licenciements au sein des équipes Dart et Flutter, des technologies étroitement liées. Ces réductions ont jeté une ombre sur l’engagement de l’entreprise envers ces outils, laissant planer le doute sur leur futur développement.</p>



<p>À cela s’ajoute l’initiative de Matt Carroll, un ancien développeur de Flutter chez Google, qui a créé un fork nommé Flock. Ce projet résulte de critiques ouvertes sur le manque d’investissement de Google et sur les réductions continues des ressources attribuées au framework. Bien que ce fork puisse offrir de nouvelles opportunités, il illustre une fragmentation potentielle de l’écosystème Flutter.</p>



<h3 class="wp-block-heading" id="h-un-potentiel-toujours-present"><strong>Un potentiel toujours présent</strong></h3>



<p>Malgré ces défis, Flutter reste une technologie populaire et puissante. Plus de 700&nbsp;000 applications ont été créées avec ce framework, parmi lesquelles des produits d’entreprises majeures. Son adoption massive et ses capacités multiplateformes en font un outil de choix pour réduire les coûts de développement et accélérer la mise sur le marché des applications.</p>



<p>En outre, la communauté open source qui soutient Flutter reste très active. De nombreux développeurs et contributeurs tiers continuent d’améliorer l’écosystème avec des plugins, des widgets et des ressources d’aide. Cette communauté pourrait, dans une certaine mesure, compenser un retrait progressif de Google.</p>



<h3 class="wp-block-heading" id="h-quels-sont-les-risques-nbsp"><strong>Quels sont les risques&nbsp;?</strong></h3>



<p>Si Google venait à réduire encore davantage ses investissements, Flutter pourrait&nbsp;:</p>



<ol start="1" class="wp-block-list">
<li><strong>Perdre en compétitivité</strong>&nbsp;: Les technologies concurrentes, comme React Native de Meta, pourraient capitaliser sur l’incertitude entourant Flutter pour attirer les développeurs.</li>



<li><strong>Subir une fragmentation</strong>&nbsp;: L’émergence de forks comme Flock pourrait diviser les efforts de développement et semer la confusion parmi les utilisateurs.</li>



<li><strong>Voir sa communauté s’essouffler</strong>&nbsp;: Un manque de vision claire et de leadership pourrait décourager les développeurs, notamment ceux qui investissent dans des projets à long terme.</li>
</ol>



<h3 class="wp-block-heading" id="h-les-raisons-d-esperer"><strong>Les raisons d’espérer</strong></h3>



<p>Cependant, l’idée d’une disparition totale de Flutter semble peu probable à court terme. La portée de son adoption et son écosystème riche lui donnent une base solide pour survivre, voire prospérer, même en cas de soutien réduit de Google. De plus, Flutter dispose d’un large réseau d’entreprises partenaires qui pourraient contribuer à son avenir.</p>



<h3 class="wp-block-heading" id="h-conclusion-nbsp-incertitude-et-opportunite"><strong>Conclusion&nbsp;: incertitude et opportunité</strong></h3>



<p>Bien que des signes indiquent une possible réduction de l’investissement de Google dans Flutter, le framework bénéficie encore d’un écosystème dynamique et de nombreux atouts. Son avenir dépendra en grande partie de&nbsp;:</p>



<ul class="wp-block-list">
<li>La stratégie future de Google.</li>



<li>L’engagement de la communauté open source.</li>



<li>La capacité de Flutter à rester compétitif face aux technologies concurrentes.</li>
</ul>



<p>Dans un monde où l’innovation et la collaboration open source jouent un rôle central, Flutter pourrait continuer à évoluer, même en dehors du giron direct de Google.</p>



<p></p>
<p>The post <a href="https://flutter-now.com/actualites/flutter-un-avenir-incertain-face-a-la-baisse-dinvestissement-de-google/">Flutter : un avenir incertain face à la baisse d&rsquo;investissement de Google ?</a> appeared first on <a href="https://flutter-now.com">FlutterNow</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://flutter-now.com/actualites/flutter-un-avenir-incertain-face-a-la-baisse-dinvestissement-de-google/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">92</post-id>	</item>
	</channel>
</rss>
