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.
  • computed correspond à un provider dérivé (Riverpod) ou un getter recalculé : l'état dérivé.
  • watch / watchEffect correspond à ref.listen (Riverpod) ou à l'écoute d'un Listenable : réagir à un changement pour déclencher un effet.
  • useState (état partagé Nuxt) correspond à un Provider global (Riverpod) ou à un ChangeNotifierProvider (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 / inject de Vue correspond au Provider d'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.

← Tous les articles