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 operationsoverkill
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.