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

  1. MyApp: Es el widget raíz de la aplicación.
  2. CounterScreen: Es un StatefulWidget que contiene el estado del contador.
  3. _CounterScreenState: Es la clase de estado que maneja el valor del contador y la lógica para incrementarlo.
  4. _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

  1. CounterProvider: Es un InheritedWidget que contiene el estado del contador y un método para incrementarlo.
  2. of: Método estático que permite a los widgets descendientes acceder al CounterProvider.
  3. CounterScreen: Es un StatelessWidget que accede al contador y al método para incrementarlo a través del CounterProvider.

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

  1. CounterProvider: Define el estado del contador y los métodos para incrementarlo y decrementarlo.
  2. FirstScreen: Muestra el contador y un botón para incrementarlo.
  3. 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

Módulo 2: Conceptos Básicos de Programación en Dart

Módulo 3: Widgets en Flutter

Módulo 4: Gestión de Estado

Módulo 5: Navegación y Enrutamiento

Módulo 6: Redes y APIs

Módulo 7: Persistencia y Almacenamiento

Módulo 8: Conceptos Avanzados de Flutter

Módulo 9: Pruebas y Depuración

Módulo 10: Despliegue y Mantenimiento

Módulo 11: Flutter para Web y Escritorio

© Copyright 2024. Todos los derechos reservados