16 juillet 2026 · Nuxt · Flutter · Gestion d'état
Gestion d'état : Nuxt face à Flutter, le parallèle qui éclaire
Pour un développeur Flutter qui apprend Nuxt (ou l'inverse) : la correspondance entre ref, computed, useState, Pinia et ValueNotifier, Provider, Riverpod, Bloc.
Je travaille des deux côtés : des applications Flutter au long cours et des projets web sous Nuxt. La bonne nouvelle pour qui connaît déjà l'un, c'est que les concepts de gestion d'état de l'autre ne sont pas nouveaux : ce sont les mêmes idées sous d'autres noms. Voici la table de correspondance que j'aurais aimé avoir en tête plus tôt.
Le socle : réactivité contre notification
La différence de fond tient au modèle de réactivité.
En Vue/Nuxt, la réactivité est implicite et automatique. Un ref est
une valeur observable, et tout ce qui la lit se réabonne tout seul. Vous
changez compteur.value, l'interface se met à jour, sans rien déclarer.
En Flutter, la réactivité est explicite. Un ValueNotifier détient une
valeur, mais il faut appeler notifyListeners() (ou assigner .value) pour
prévenir, et un widget doit s'abonner explicitement (via ValueListenableBuilder,
ListenableBuilder, ou le watch de Riverpod).
Retenez cette phrase : Vue observe, Flutter notifie. Tout le reste en découle.
La brique de base
// Nuxt / Vue
const compteur = ref(0)
const doubler = computed(() => compteur.value * 2)
compteur.value++ // l'UI qui lit compteur se met à jour
// Flutter
final compteur = ValueNotifier<int>(0);
compteur.value++; // les ValueListenableBuilder abonnés se reconstruisent
Le ref de Vue correspond au ValueNotifier de Flutter : une valeur
observable. Le computed de Vue, une valeur dérivée qui se recalcule seule,
correspond à un Provider dérivé de Riverpod ou à un getter recalculé. Dans
les deux mondes, on ne recopie jamais une valeur dérivée à la main : on la
déclare comme dépendant d'une autre.
La table de correspondance
ref/reactive(Vue) correspond àValueNotifier/ChangeNotifier(Flutter) : la valeur observable de base.computedcorrespond à un provider dérivé (Riverpod) ou un getter recalculé : l'état dérivé.watch/watchEffectcorrespond àref.listen(Riverpod) ou à l'écoute d'unListenable: réagir à un changement pour déclencher un effet.useState(état partagé Nuxt) correspond à unProviderglobal (Riverpod) ou à unChangeNotifierProvider(Provider) : un état accessible depuis plusieurs endroits.- Pinia (store avec state, actions, getters) correspond à Riverpod structuré ou à un ensemble de Cubit/BLoC : le domaine métier avec sa logique.
- L'injection par
provide/injectde Vue correspond auProviderd'arbre de widgets de Flutter : fournir une dépendance à un sous-arbre.
Un store, deux écritures
Un panier, côté Pinia :
// stores/panier.ts
export const usePanier = defineStore('panier', () => {
const articles = ref<Article[]>([])
const total = computed(() =>
articles.value.reduce((s, a) => s + a.prix, 0)
)
function ajouter(a: Article) { articles.value.push(a) }
return { articles, total, ajouter }
})
Le même panier, côté Cubit (flutter_bloc) :
class PanierCubit extends Cubit<List<Article>> {
PanierCubit() : super([]);
int get total => state.fold(0, (s, a) => s + a.prix);
void ajouter(Article a) => emit([...state, a]);
}
La forme diffère (Pinia expose un objet réactif, le Cubit émet un nouvel
état immuable), mais la structure est identique : un état, une valeur
dérivée (total), une action (ajouter). Un développeur Flutter lit le
store Pinia sans effort, et réciproquement.
La dimension que Flutter n'a pas : le serveur
Il y a un endroit où le parallèle s'arrête, et c'est important. Nuxt fait du
rendu serveur : le même code de gestion d'état s'exécute côté serveur puis
côté client. D'où la nécessité de useState plutôt qu'un simple ref
global, pour isoler l'état par requête et éviter qu'un visiteur voie les
données d'un autre (j'en parle dans l'article sur les bonnes pratiques).
Flutter n'a pas ce problème : une application tourne sur un seul appareil, pour un seul utilisateur. Un singleton d'état y est parfaitement légitime. En Nuxt, le même singleton est un bug de sécurité. C'est la principale adaptation mentale à faire en passant de Flutter au web rendu côté serveur.
La leçon
Ne réapprenez pas la gestion d'état, traduisez-la. « Observer une valeur », « en dériver une autre », « réagir à un changement », « partager un domaine » : ces quatre gestes existent des deux côtés, avec des noms différents. Gardez en tête que Vue observe quand Flutter notifie, et n'oubliez jamais la dimension serveur de Nuxt, absente de Flutter. Avec ces deux repères, passer d'un monde à l'autre devient une simple traduction, pas un nouvel apprentissage.