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):
setStateen 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,Desafío
DifícilHaz que el interruptor del modo oscuro cambie toda la aplicación:
- Añade
final darkModeProvider = StateProvider((ref) => false);en la parte superior. - Convierte
MyAppen unConsumerWidgetcuyothemeModesiga al proveedor. - Convierte
SettingsScreenen unConsumerWidget: 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;
});
},
),
],
),
);
}
}
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
1Elevar el estado
Pasar el estado a un componente hijoPasar una función callback a un componente hijoCallbacks que envían un valorElevar el estado4Riverpod
Riverpod: ProviderScopeConsumerWidget y ref.watchRiverpod: StateProviderRiverpod: NotifierProveedores que observan otros proveedoresElegir dónde vive el estadoPractica por tu cuenta: Compilador de Flutter online