BLoC with Cubit in Flutter: solved exercise

BLoC with Cubit in Flutter: solved exercise

Cubit is the streamlined version of the BLoC pattern: it manages state by emitting typed values without requiring explicit event definitions. It is ideal for UI logic that does not need event histories or complex side effects.

Problem statement

Build a counter with three operations (increment, decrement, reset) using Cubit. The UI must react to every state change automatically without a single setState.

Dependencies

1
2
dependencies:
  flutter_bloc: ^8.1.6

Flutter solution

 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 – Counter')),
      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('Subtract'),
                    ),
                    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('Add'),
                    ),
                  ],
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

Expected result

  • Counter starts at 0.
  • Each button calls a cubit method and the UI rebuilds automatically.
  • No setState appears anywhere in the screen widget.

Common mistakes

  • Creating the cubit inside the widget instead of providing it with BlocProvider: the cubit is destroyed and recreated on every rebuild.
  • Using context.read inside build (it is only safe in callbacks); use context.watch or BlocBuilder to observe state.
  • Emitting the same primitive value: Cubit compares by equality and silently discards duplicates, which can be confusing when state is a mutable object.

When to use Cubit vs BLoC

SituationCubitBLoC
Simple state, few operations✅overkill
Event history, audit logging❌✅
Complex side effects per event❌✅
External streams feeding state❌✅

Practical use

Cubit is the natural entry point into the BLoC ecosystem. It is used in production apps for forms, shopping carts, filters, user preferences, and any feature-level state that does not need event traceability.

Guided practice and next step

FAQ

Is Cubit officially part of flutter_bloc?

Yes. Cubit is included in the flutter_bloc package maintained by Felix Angelov and is fully documented.

Can I migrate from Cubit to BLoC without rewriting the UI?

Yes. BlocBuilder works the same with both. Only the class managing the state changes.

Do I need a BlocObserver from day one?

Not required, but adding a global BlocObserver in development to log all state transitions is a highly recommended practice.