Diferencias entre Stateless y Stateful Widgets

En Flutter, entender la diferencia entre StatelessWidget y StatefulWidget es fundamental para construir aplicaciones eficientes. Cada tipo tiene su propósito específico y elegir el correcto impacta directamente en el rendimiento.

¿Qué es un StatelessWidget?

  StatelessWidget

Un StatelessWidget es inmutable. Una vez creado, no puede cambiar su estado interno. Es perfecto para UI estática que no necesita actualizarse.

  Características principales

  • Inmutable: No puede cambiar después de ser creado
  • Sin estado interno: No mantiene datos que cambien
  • Rendimiento óptimo: Se renderiza una sola vez
  • Método build(): Se ejecuta solo cuando es necesario

dart
class MiTextoEstatico extends StatelessWidget {
final String texto;

const MiTextoEstatico({Key? key, required this.texto}) : super(key: key);


Widget build(BuildContext context) {
  return Text(
    texto,
    style: TextStyle(fontSize: 18),
  );
}
}

¿Qué es un StatefulWidget?

  StatelessWidget

Un StatefulWidget puede cambiar su estado interno durante su ciclo de vida. Ideal para UI dinámica que responde a interacciones del usuario.

  Características principales

  • Mutable: Puede cambiar su estado interno
  • Método setState(): Actualiza la UI cuando cambia el estado
  • Ciclo de vida: Tiene métodos como initState(), dispose()
  • Dos clases: Widget + State

dart
class Contador extends StatefulWidget {

_ContadorState createState() => _ContadorState();
}

class _ContadorState extends State<Contador> {
int _numero = 0;

void _incrementar() {
  setState(() {
    _numero++;
  });
}


Widget build(BuildContext context) {
  return Column(
    children: [
      Text('Contador: $_numero'),
      ElevatedButton(
        onPressed: _incrementar,
        child: Text('Incrementar'),
      ),
    ],
  );
}
}
  Importante

Regla de oro: Si no necesitas cambiar datos, usa StatelessWidget. Si necesitas actualizar la UI, usa StatefulWidget.

Cuándo usar cada uno


Usa StatelessWidget para:

  Nota

  • Textos estáticos
  • Iconos y imágenes
  • Layouts que no cambian
  • Widgets de presentación

dart
// Perfecto para StatelessWidget
class TarjetaPerfil extends StatelessWidget {
final String nombre;
final String email;

const TarjetaPerfil({Key? key, required this.nombre, required this.email}) 
  : super(key: key);


Widget build(BuildContext context) {
  return Card(
    child: ListTile(
      leading: Icon(Icons.person),
      title: Text(nombre),
      subtitle: Text(email),
    ),
  );
}
}

Usa StatefulWidget para:

  Nota

  • Formularios con validación
  • Contadores y temporizadores
  • Listas que se actualizan
  • Animaciones

dart
// Necesita StatefulWidget
class FormularioLogin extends StatefulWidget {

_FormularioLoginState createState() => _FormularioLoginState();
}

class _FormularioLoginState extends State<FormularioLogin> {
final _emailController = TextEditingController();
bool _cargando = false;

void _iniciarSesion() {
  setState(() {
    _cargando = true;
  });
  // Lógica de login...
}


Widget build(BuildContext context) {
  return Column(
    children: [
      TextField(
        controller: _emailController,
        decoration: InputDecoration(labelText: 'Email'),
      ),
      ElevatedButton(
        onPressed: _cargando ? null : _iniciarSesion,
        child: _cargando 
          ? CircularProgressIndicator() 
          : Text('Iniciar Sesión'),
      ),
    ],
  );
}


void dispose() {
  _emailController.dispose();
  super.dispose();
}
}

Ejemplo práctico: Lista de tareas


Combinando ambos tipos para máxima eficiencia:

dart
// StatefulWidget para la lista (datos cambian)
class ListaTareas extends StatefulWidget {

_ListaTareasState createState() => _ListaTareasState();
}

class _ListaTareasState extends State<ListaTareas> {
List<String> _tareas = ['Estudiar Flutter', 'Hacer ejercicio'];

void _agregarTarea(String tarea) {
  setState(() {
    _tareas.add(tarea);
  });
}


Widget build(BuildContext context) {
  return ListView.builder(
    itemCount: _tareas.length,
    itemBuilder: (context, index) {
      // StatelessWidget para cada item (no cambia individualmente)
      return TareaItem(tarea: _tareas[index]);
    },
  );
}
}

// StatelessWidget para items individuales
class TareaItem extends StatelessWidget {
final String tarea;

const TareaItem({Key? key, required this.tarea}) : super(key: key);


Widget build(BuildContext context) {
  return ListTile(
    leading: Icon(Icons.check_box_outline_blank),
    title: Text(tarea),
  );
}
}

Buenas prácticas


dart
// ✅ Correcto: Widget estático extraído
class PantallaContador extends StatefulWidget {

_PantallaContadorState createState() => _PantallaContadorState();
}

class _PantallaContadorState extends State<PantallaContador> {
int _contador = 0;


Widget build(BuildContext context) {
  return Scaffold(
    appBar: const BarraSuperiorEstatica(), // StatelessWidget
    body: Column(
      children: [
        Text('Contador: $_contador'), // Solo esto cambia
        const TextoEstatico(), // StatelessWidget
      ],
    ),
    floatingActionButton: FloatingActionButton(
      onPressed: () => setState(() => _contador++),
      child: Icon(Icons.add),
    ),
  );
}
}

class BarraSuperiorEstatica extends StatelessWidget {
const BarraSuperiorEstatica({Key? key}) : super(key: key);


Widget build(BuildContext context) {
  return AppBar(title: Text('Mi App'));
}
}

Evitar


dart
// ❌ Incorrecto: Todo en StatefulWidget innecesariamente
class PantallaContador extends StatefulWidget {

_PantallaContadorState createState() => _PantallaContadorState();
}

class _PantallaContadorState extends State<PantallaContador> {
int _contador = 0;


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text('Mi App')), // Se re-renderiza innecesariamente
    body: Column(
      children: [
        Text('Contador: $_contador'),
        Text('Texto que nunca cambia'), // Se re-renderiza innecesariamente
      ],
    ),
  );
}
}
  Resumen

StatelessWidget: Para UI que no cambia. Más rápido y simple. StatefulWidget: Para UI dinámica que responde a cambios. Más potente pero complejo.