En este tema, exploraremos dos métodos fundamentales para la gestión de estado en Flutter: setState
e InheritedWidget
. Ambos son esenciales para construir aplicaciones Flutter reactivas y eficientes.
¿Qué es el Estado en Flutter?
El estado en Flutter se refiere a cualquier dato que pueda cambiar durante la vida útil de una aplicación. Por ejemplo, el contenido de un formulario, la posición de un scroll, o la autenticación del usuario.
setState
setState
es el método más básico y directo para gestionar el estado en Flutter. Se utiliza dentro de un StatefulWidget
para notificar al framework que el estado del widget ha cambiado y que debe reconstruirse.
Ejemplo Práctico con setState
Vamos a crear un contador simple que incrementa su valor cada vez que se presiona un botón.
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: CounterScreen(), ); } } class CounterScreen extends StatefulWidget { @override _CounterScreenState createState() => _CounterScreenState(); } class _CounterScreenState extends State<CounterScreen> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Contador con setState'), ), body: Center( child: Text( 'Contador: $_counter', style: TextStyle(fontSize: 24), ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, child: Icon(Icons.add), ), ); } }
Explicación del Código
- MyApp: Es el widget raíz de la aplicación.
- CounterScreen: Es un
StatefulWidget
que contiene el estado del contador. - _CounterScreenState: Es la clase de estado que maneja el valor del contador y la lógica para incrementarlo.
- _incrementCounter: Método que llama a
setState
para actualizar el valor del contador y reconstruir el widget.
Ventajas y Desventajas de setState
Ventajas:
- Simple y fácil de entender.
- Ideal para estados locales y simples.
Desventajas:
- No es adecuado para estados complejos o compartidos entre múltiples widgets.
- Puede llevar a una reconstrucción innecesaria de widgets, afectando el rendimiento.
InheritedWidget
InheritedWidget
es una forma más avanzada de gestionar el estado, especialmente útil cuando necesitas compartir datos entre múltiples widgets en el árbol de widgets.
Ejemplo Práctico con InheritedWidget
Vamos a crear un contador que puede ser accedido y modificado desde diferentes partes de la aplicación.
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return CounterProvider( child: MaterialApp( home: CounterScreen(), ), ); } } class CounterProvider extends InheritedWidget { final int counter; final Function() incrementCounter; CounterProvider({Key? key, required Widget child}) : counter = 0, incrementCounter = () {}, super(key: key, child: child); @override bool updateShouldNotify(CounterProvider oldWidget) { return oldWidget.counter != counter; } static CounterProvider? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<CounterProvider>(); } } class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { final counterProvider = CounterProvider.of(context); return Scaffold( appBar: AppBar( title: Text('Contador con InheritedWidget'), ), body: Center( child: Text( 'Contador: ${counterProvider?.counter}', style: TextStyle(fontSize: 24), ), ), floatingActionButton: FloatingActionButton( onPressed: counterProvider?.incrementCounter, child: Icon(Icons.add), ), ); } }
Explicación del Código
- CounterProvider: Es un
InheritedWidget
que contiene el estado del contador y un método para incrementarlo. - of: Método estático que permite a los widgets descendientes acceder al
CounterProvider
. - CounterScreen: Es un
StatelessWidget
que accede al contador y al método para incrementarlo a través delCounterProvider
.
Ventajas y Desventajas de InheritedWidget
Ventajas:
- Ideal para compartir estado entre múltiples widgets.
- Más eficiente en términos de rendimiento para estados complejos.
Desventajas:
- Más complejo de implementar y entender.
- No es tan directo como
setState
para estados simples.
Ejercicio Práctico
Ejercicio
Crea una aplicación que tenga dos pantallas. La primera pantalla debe mostrar un contador y un botón para incrementarlo. La segunda pantalla debe mostrar el mismo contador y un botón para decrementarlo. Utiliza InheritedWidget
para compartir el estado del contador entre ambas pantallas.
Solución
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return CounterProvider( child: MaterialApp( home: FirstScreen(), ), ); } } class CounterProvider extends InheritedWidget { final int counter; final Function() incrementCounter; final Function() decrementCounter; CounterProvider({Key? key, required Widget child}) : counter = 0, incrementCounter = () {}, decrementCounter = () {}, super(key: key, child: child); @override bool updateShouldNotify(CounterProvider oldWidget) { return oldWidget.counter != counter; } static CounterProvider? of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<CounterProvider>(); } } class FirstScreen extends StatelessWidget { @override Widget build(BuildContext context) { final counterProvider = CounterProvider.of(context); return Scaffold( appBar: AppBar( title: Text('Primera Pantalla'), ), body: Center( child: Text( 'Contador: ${counterProvider?.counter}', style: TextStyle(fontSize: 24), ), ), floatingActionButton: FloatingActionButton( onPressed: counterProvider?.incrementCounter, child: Icon(Icons.add), ), ); } } class SecondScreen extends StatelessWidget { @override Widget build(BuildContext context) { final counterProvider = CounterProvider.of(context); return Scaffold( appBar: AppBar( title: Text('Segunda Pantalla'), ), body: Center( child: Text( 'Contador: ${counterProvider?.counter}', style: TextStyle(fontSize: 24), ), ), floatingActionButton: FloatingActionButton( onPressed: counterProvider?.decrementCounter, child: Icon(Icons.remove), ), ); } }
Explicación del Ejercicio
- CounterProvider: Define el estado del contador y los métodos para incrementarlo y decrementarlo.
- FirstScreen: Muestra el contador y un botón para incrementarlo.
- SecondScreen: Muestra el mismo contador y un botón para decrementarlo.
Conclusión
En esta lección, hemos aprendido sobre dos métodos fundamentales para la gestión de estado en Flutter: setState
e InheritedWidget
. Mientras que setState
es ideal para estados simples y locales, InheritedWidget
es más adecuado para estados complejos y compartidos entre múltiples widgets. Con esta base, estarás mejor preparado para manejar el estado en tus aplicaciones Flutter de manera eficiente y efectiva.
Curso de Desarrollo con Flutter
Módulo 1: Introducción a Flutter
- ¿Qué es Flutter?
- Configuración del Entorno de Desarrollo
- Entendiendo la Arquitectura de Flutter
- Creando Tu Primera App con Flutter
Módulo 2: Conceptos Básicos de Programación en Dart
- Introducción a Dart
- Variables y Tipos de Datos
- Sentencias de Control de Flujo
- Funciones y Métodos
- Programación Orientada a Objetos en Dart
Módulo 3: Widgets en Flutter
- Introducción a los Widgets
- Widgets Stateless vs Stateful
- Widgets Básicos
- Widgets de Diseño
- Widgets de Entrada y Formularios
Módulo 4: Gestión de Estado
Módulo 5: Navegación y Enrutamiento
- Introducción a la Navegación
- Navegación Básica
- Rutas Nombradas
- Pasando Datos Entre Pantallas
- Deep Linking
Módulo 6: Redes y APIs
- Obteniendo Datos de Internet
- Parseo de Datos JSON
- Manejo de Errores de Red
- Usando APIs REST
- Integración con GraphQL
Módulo 7: Persistencia y Almacenamiento
- Introducción a la Persistencia
- Preferencias Compartidas
- Almacenamiento de Archivos
- Base de Datos SQLite
- Usando Hive para Almacenamiento Local
Módulo 8: Conceptos Avanzados de Flutter
- Animaciones en Flutter
- Custom Paint y Canvas
- Canales de Plataforma
- Isolates y Concurrencia
- Optimización de Rendimiento
Módulo 9: Pruebas y Depuración
- Introducción a las Pruebas
- Pruebas Unitarias
- Pruebas de Widgets
- Pruebas de Integración
- Técnicas de Depuración
Módulo 10: Despliegue y Mantenimiento
- Preparación para el Lanzamiento
- Construcción para iOS
- Construcción para Android
- Integración Continua/Despliegue Continuo (CI/CD)
- Mantenimiento y Actualización de Tu App