BLoC con Cubit en Flutter: ejercicio resuelto

BLoC con Cubit en Flutter: ejercicio resuelto

Cubit es la versión simplificada del patrón BLoC: gestiona estado emitiendo valores tipados sin necesidad de definir eventos explícitos. Es ideal para lógica de UI que no requiere historiales de eventos ni side effects complejos.

Enunciado

Implementa un contador con tres operaciones (incrementar, decrementar, reiniciar) usando Cubit. La UI debe reaccionar automáticamente a cada cambio de estado sin setState.

Dependencias

1
2
dependencies:
  flutter_bloc: ^8.1.6

Solución en Flutter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

// ── Cubit ────────────────────────────────────────────────────────────────────
class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
  void decrement() => emit(state - 1);
  void reset() => emit(0);
}

// ── App ──────────────────────────────────────────────────────────────────────
void main() {
  runApp(
    BlocProvider(
      create: (_) => CounterCubit(),
      child: const MaterialApp(home: CounterPage()),
    ),
  );
}

// ── UI ───────────────────────────────────────────────────────────────────────
class CounterPage extends StatelessWidget {
  const CounterPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Cubit – Contador')),
      body: Center(
        child: BlocBuilder<CounterCubit, int>(
          builder: (context, count) {
            return Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  '$count',
                  style: Theme.of(context).textTheme.displayLarge,
                ),
                const SizedBox(height: 24),
                Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    FilledButton.icon(
                      onPressed: () => context.read<CounterCubit>().decrement(),
                      icon: const Icon(Icons.remove),
                      label: const Text('Restar'),
                    ),
                    const SizedBox(width: 8),
                    OutlinedButton(
                      onPressed: () => context.read<CounterCubit>().reset(),
                      child: const Text('Reset'),
                    ),
                    const SizedBox(width: 8),
                    FilledButton.icon(
                      onPressed: () => context.read<CounterCubit>().increment(),
                      icon: const Icon(Icons.add),
                      label: const Text('Sumar'),
                    ),
                  ],
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

Resultado esperado

  • El contador arranca en 0.
  • Cada botón dispara un método del cubit y la UI se reconstruye sola.
  • No hay ni un setState en la pantalla.

Errores frecuentes

  • Crear el cubit dentro del widget en lugar de proveerlo con BlocProvider: el cubit se destruye y recrea con cada rebuild.
  • Usar context.read dentro de build (solo para callbacks); usar context.watch o BlocBuilder para leer el estado.
  • Emitir el mismo valor cuando el estado es un primitivo: Cubit compara por igualdad y descarta duplicados, lo que puede confundir si el estado es un objeto mutable.

Cuándo usar Cubit vs BLoC

SituaciónCubitBLoC
Estado simple, pocas operacionesinnecesario
Historial de eventos, logging
Side effects complejos por evento
Streams externos que alimentan estado

Aplicación práctica

Cubit es el punto de entrada natural al ecosistema BLoC. Se usa en apps de producción para formularios, carrito de compra, filtros, preferencias de usuario y cualquier estado local de feature que no necesita trazabilidad de eventos.

Siguiente ejercicio recomendado

Práctica guiada y siguiente paso

FAQ

¿Cubit es oficial del equipo de flutter_bloc?

Sí. Cubit forma parte del paquete flutter_bloc de Felix Angelov y está documentado y mantenido oficialmente.

¿Puedo migrar de Cubit a BLoC sin reescribir la UI?

Sí. El widget BlocBuilder funciona igual con ambos. Solo cambia la clase que gestiona el estado.

¿Necesito BlocObserver desde el principio?

No es obligatorio, pero añadir un BlocObserver global en desarrollo para loguear transiciones de estado es una práctica muy recomendable.