Skip to content

StatefulWidget et cycle de vie des widgets

Un StatefulWidget est un widget qui peut changer d’état au cours de sa durée de vie. Contrairement aux StatelessWidget, ils peuvent maintenir des données mutables et réagir aux interactions utilisateur.

Voici comment créer un StatefulWidget basique avec un compteur :

import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(home: MainScreen()));
}
class MainScreen extends StatefulWidget {
const MainScreen({Key? key}) : super(key: key);
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int value = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() {
value++;
}),
child: const Icon(Icons.add),
),
body: Center(
child: Text('$value'),
),
);
}
}

Les StatefulWidget disposent de plusieurs méthodes de cycle de vie qui permettent de contrôler leur comportement :

class _MainScreenState extends State<MainScreen> with SingleTickerProviderStateMixin {
final tabs = const [
Tab(text: 'Livres'),
Tab(text: 'Films'),
Tab(text: 'Disques'),
];
late TabController tabController;
@override
void initState() {
// Appelée une seule fois lors de la création du widget
tabController = TabController(
length: tabs.length,
initialIndex: 0,
vsync: this,
);
super.initState();
}
@override
void dispose() {
// Appelée lors de la destruction du widget
tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
bottom: TabBar(
controller: tabController,
tabs: tabs,
isScrollable: true,
),
),
body: TabBarView(
controller: tabController,
children: const <Widget>[
Center(child: Text('Livres')),
Center(child: Text('Films')),
Center(child: Text('Musique')),
],
),
);
}
}
  • initState() : Appelée une seule fois lors de la création du widget
  • build() : Appelée chaque fois que le widget doit être reconstruit
  • dispose() : Appelée lors de la destruction du widget pour libérer les ressources

La méthode setState() est le mécanisme de base pour mettre à jour l’état d’un widget :

void _incrementCounter() {
setState(() {
_counter++;
});
}
  1. Appelez setState seulement quand nécessaire : Ne pas l’utiliser si aucune propriété visible ne change
  2. Gardez le code dans setState minimal : Seules les modifications d’état doivent y être placées
  3. Évitez les opérations coûteuses : Les calculs lourds doivent être faits avant l’appel à setState

L’utilisation de setState présente plusieurs limitations importantes :

Problèmes de séparation des préoccupations

Section titled “Problèmes de séparation des préoccupations”
  • Séparation Vue/Logique difficile : Le code métier est mélangé avec l’interface utilisateur
  • Testabilité réduite : Difficile de tester la logique indépendamment de l’interface
  • Maintenance complexe : Les modifications deviennent difficiles à gérer
  • Reconstructions inutiles : setState déclenche un rebuild complet du widget
  • Scalabilité limitée : Devient rapidement complexe dans de grandes applications
  • Gestion d’état globale impossible : Difficile de partager l’état entre plusieurs widgets
class _ProblematicScreenState extends State<ProblematicScreen> {
bool isLoading = false;
List<Item> items = [];
String? errorMessage;
// Logique métier mélangée avec l'interface
Future<void> loadItems() async {
setState(() {
isLoading = true;
errorMessage = null;
});
try {
final response = await ApiService.getItems();
setState(() {
items = response.items;
isLoading = false;
});
} catch (e) {
setState(() {
errorMessage = e.toString();
isLoading = false;
});
}
}
@override
Widget build(BuildContext context) {
// Interface utilisateur complexe difficile à tester
return Scaffold(/* ... */);
}
}

L’utilisation de setState rend difficile la séparation Vue/Logique et par conséquent la testabilité.

De nombreux packages tentent de proposer une solution pour organiser les applications Flutter, en s’inspirant de différents patterns :

  • Bloc : stream et réactivité
  • Provider : notifiers et réactivité
  • Mobx : Réactivité et ViewModel
  • Redux : global store & unidirectional data flow

Les StatefulWidget et setState sont parfaits pour :

  • Applications simples avec peu d’état
  • Composants isolés sans partage d’état
  • Prototypage rapide et apprentissage

Pour des applications plus complexes, il est recommandé d’utiliser des solutions de gestion d’état plus avancées comme les Notifiers ou le package Provider.