Menu
CoddyTech

Elegir dónde vive el estado

Parte de la sección Gestión del estado del Journey de Flutter de Coddy. Lección 21 de 27.

El interruptor del modo oscuro en la pantalla Settings se mueve, pero nada se oscurece. Conserva su valor con setState, y solo el interruptor puede verlo. El tema se establece muy lejos, en MyApp. Este valor pertenece a toda la aplicación.

Todo valor de una aplicación necesita un lugar. Pregúntate quién lo usa:

  • Un widget (el texto de un campo, un panel abierto, el índice de una pestaña): setState en ese widget.
  • Unos pocos widgets cercanos (un botón y la etiqueta que tiene al lado): súbelo a su elemento padre y pásalo hacia abajo.
  • Muchos widgets o varias pantallas (el usuario, un carrito, la configuración): un modelo compartido con Provider o Riverpod.

¿Provider o Riverpod? Ambos cumplen su función. Provider es un poco más sencillo y coloca los modelos en el árbol de widgets, donde se encuentran mediante context. Riverpod mantiene los proveedores fuera del árbol, así que un valor no necesita context, y los proveedores pueden observarse entre sí. Elige uno para una aplicación y úsalo en todas partes.

Aquí encaja un StateProvider: un bool, leído por MyApp para elegir el tema y modificado por el interruptor:

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

// en MyApp, ahora un ConsumerWidget:
themeMode: ref.watch(darkModeProvider) ? ThemeMode.dark : ThemeMode.light,
challenge icon

Desafío

Difícil

Haz que el interruptor del modo oscuro cambie toda la aplicación:

  • Añade final darkModeProvider = StateProvider((ref) => false); en la parte superior.
  • Convierte MyApp en un ConsumerWidget cuyo themeMode siga al proveedor.
  • Convierte SettingsScreen en un ConsumerWidget: el interruptor muestra el valor del proveedor y lo establece cuando cambia.

Pruébalo tú mismo

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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Gestión del estado

Practica por tu cuenta: Compilador de Flutter online