Stokky est une application Flutter, écrite en Dart, avec Bloc pour la gestion d'état. Cette page décrit les choix techniques qui la font tenir : le stockage local comme source de vérité, une synchronisation qui traite les conflits au lieu de les écraser, et des correctifs livrés sans passer par un magasin d'applications.
Le stockage local n'est pas un cache : c'est la source de vérité. L'application s'ouvre sans compte et sans réseau, et chaque lecture comme chaque écriture passe par une base SQL embarquée sur l'appareil. L'interface n'attend jamais un aller-retour serveur — le réseau, quand il existe, ne fait que rattraper ce qui a déjà été décidé localement. C'est le cas sur téléphone et sur ordinateur ; la version web, elle, travaille en ligne, avec un compte.
Démarrage immédiat, sans inscription
Les écritures sont validées localement, puis voyagent
Le mode purement local reste utilisable indéfiniment
Synchronisation
Chaque modification entre dans une file d'attente durable qui survit à la fermeture de l'application. La récupération est incrémentale : un curseur retient l'endroit où la dernière passe s'est arrêtée, donc seules les lignes réellement modifiées circulent. En cas d'échec, la modification reste dans la file et repart à la prochaine écriture, au retour du réseau, ou dans la minute. L'état reste visible : la déconnexion prévient s'il reste des modifications à envoyer, un bandeau signale une synchronisation bloquée. Les photos suivent leur propre chemin, séparé du texte, parce qu'elles n'ont ni la même taille ni la même urgence.
File d'attente persistée, rejouée au redémarrage
Récupération incrémentale par curseur, jamais de rechargement complet
Reprise automatique, aucune perte silencieuse
Les photos se synchronisent sur leur propre voie
Gestion des conflits
Une écriture ne remplace jamais une ligne à l'aveugle. Elle porte la version qu'elle croit corriger ; si la ligne a bougé ailleurs entre-temps, le serveur refuse, l'application capture les deux états et met un conflit en file. Un écran de résolution montre la différence champ par champ — ma version, la version distante — en séparant ce qui a changé de ce qui n'a pas bougé. Là où aucune fusion n'aurait de sens, comme les zones dessinées sur la photo d'un meuble, la dernière écriture l'emporte automatiquement : arbitrer aurait coûté à l'utilisateur plus que ça ne lui aurait rapporté.
Écriture conditionnelle : contrôle de version ligne par ligne
Le conflit est mis de côté, l'application continue
Résolution champ par champ, choix explicite
Dernière écriture gagnante là où arbitrer serait absurde
Le reste de la mécanique
Ce qui rend les trois premiers points tenables.
Temps réel
Quand plusieurs personnes partagent un foyer, les changements arrivent sans rafraîchir. Un abonnement temps réel réveille la synchronisation dès qu'une ligne bouge côté serveur, au lieu d'interroger en boucle.
Mises à jour à distance
Un correctif atteint les appareils déjà installés sans passer par la revue d'un magasin d'applications, en quelques heures plutôt qu'en quelques jours. Chaque correctif est validé sur un canal de préproduction avant d'être diffusé.
Architecture
MVVM par fonctionnalité, avec des dépôts abstraits pour seule couture. Les dépendances vont dans un seul sens : vue → cubit → dépôt. C'est cette couture qui permet au web de tourner sans base locale, et à chaque test de cubit de tourner sans base du tout.
État prévisible
L'état vit dans des Cubits, sous forme d'états immuables plutôt que de booléens de chargement éparpillés. Ce qui ne survivrait pas à la reconstruction d'un écran reste un simple setState : la frontière est écrite quelque part, pas devinée à chaque écran.
Tests sur sept niveaux
Cubit, dépôt, widget, intégration, performance, parcours de bout en bout dans l'application réelle, et un robot qui explore l'application tout seul à la recherche d'erreurs et de textes coupés ou non traduits. Le cycle est rouge-vert-refactor : le test échoue d'abord, sinon il ne prouve rien. Les captures d'écran de la page d'accueil sont produites par ces mêmes tests — elles ne peuvent pas mentir sur l'application.
Schéma additif
Une migration ajoute une colonne ; elle n'en renomme ni n'en supprime jamais. Une version ancienne de l'application continue donc de fonctionner contre un serveur récent — condition indispensable dès lors que tout le monde ne met pas à jour le même jour.
Design system
Couleurs, espacements, rayons et typographie sont des jetons, jamais des valeurs écrites à la main dans un widget. Un catalogue de composants partagés les montre isolés, en clair et en sombre. Cette page utilise les mêmes jetons que l'application.
Une base, six cibles
Android, iOS, le web et les trois systèmes de bureau partent du même code. Le widget d'écran d'accueil est de l'interface de l'application rendue en image : en changer la mise en page reste du Dart, pas du code natif à écrire deux fois.
Trois langues, aucune en dur
Français, anglais et malgache, sans une seule chaîne écrite en dur dans un widget. Le framework ne fournit aucune traduction malgache pour ses propres libellés : ils retombent sur le français plutôt que sur l'anglais, ce qui est le repli utile ici.
Livraison
Trois environnements s'installent côte à côte sur le même téléphone, chacun avec sa propre base locale, ce qui permet de tester une version sans toucher à ses vraies données. Rien n'atteint la production sans être passé par la préproduction d'abord.
0Plateformes
0Langues
0Niveaux de tests
0Traceurs
Rien de tout cela ne se voit. C'est le but.
L'utilisateur ouvre l'application, ajoute un article, ferme le téléphone. Le reste est notre problème.