Menu
CoddyTech

Choisir où réside l’état

Fait partie de la section Gestion d’état du Journey Flutter de Coddy. Leçon 21 sur 27.

L’interrupteur du mode sombre sur l’écran des paramètres bouge, mais rien ne passe en sombre. Il conserve sa valeur avec setState, et seul l’interrupteur peut la voir. Le thème est défini bien plus loin, dans MyApp. Cette valeur concerne toute l’application.

Chaque valeur d’une application a besoin d’un emplacement. Demande-toi qui l’utilise :

  • Un seul widget (le texte d’un champ, un panneau ouvert, l’index d’un onglet) : setState dans ce widget.
  • Quelques widgets proches (un bouton et le libellé juste à côté) : remonte-la jusqu’à leur parent et transmets-la.
  • De nombreux widgets ou plusieurs écrans (l’utilisateur, un panier, les paramètres) : un modèle partagé avec Provider ou Riverpod.

Provider ou Riverpod ? Les deux font l’affaire. Provider est un peu plus simple et place les modèles dans l’arbre des widgets, où ils sont accessibles via context. Riverpod conserve les providers en dehors de l’arbre, ainsi une valeur n’a pas besoin de context, et les providers peuvent s’observer entre eux. Choisis-en un pour une application et utilise-le partout.

Ici, un StateProvider convient : un bool, lu par MyApp pour choisir le thème et modifié par l’interrupteur :

final darkModeProvider = StateProvider((ref) => false);

// dans MyApp, désormais un ConsumerWidget :
themeMode: ref.watch(darkModeProvider) ? ThemeMode.dark : ThemeMode.light,
challenge icon

Défi

Difficile

Fais en sorte que le commutateur du mode sombre modifie toute l’application :

  • Ajoute final darkModeProvider = StateProvider((ref) => false); en haut.
  • Transforme MyApp en ConsumerWidget dont le themeMode suit le fournisseur.
  • Transforme SettingsScreen en ConsumerWidget : le commutateur affiche la valeur du fournisseur et la définit lorsqu’il est actionné.

Essayez vous-même

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

void main() {
  runApp(const ProviderScope(child: MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: ThemeMode.light,
      home: const HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Notes'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const SettingsScreen()),
              );
            },
          ),
        ],
      ),
      body: const Center(child: Text('No notes yet')),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool dark = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Settings')),
      body: ListView(
        children: [
          SwitchListTile(
            title: const Text('Dark mode'),
            value: dark,
            onChanged: (value) {
              setState(() {
                dark = value;
              });
            },
          ),
        ],
      ),
    );
  }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Gestion d’état

Entraînez-vous par vous-même : Compilateur Flutter en ligne