iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Bloc / Cubit

BLoC (Business Logic Component) separates UI from state by routing events through a Bloc class that emits state objects. Widgets dispatch events (`context.read().add(...)`) and rebuild from state (`BlocBuilder`). It scales well for non-trivial apps because every state change is explicit and traceable.

A counter and an async load Bloc

EXAMPLE
// pubspec.yaml -> flutter_bloc: ^8.1.0

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

// ---------- Counter Bloc ----------
sealed class CounterEvent {}
class Increment extends CounterEvent {}
class Reset extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((e, emit) => emit(state + 1));
    on<Reset>((e, emit) => emit(0));
  }
}

// ---------- Async load Bloc with states as a sealed class ----------
sealed class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserLoaded extends UserState { final String name; UserLoaded(this.name); }
class UserError extends UserState { final String message; UserError(this.message); }

sealed class UserEvent {}
class LoadUser extends UserEvent { final String id; LoadUser(this.id); }

class UserBloc extends Bloc<UserEvent, UserState> {
  UserBloc() : super(UserInitial()) {
    on<LoadUser>((e, emit) async {
      emit(UserLoading());
      try {
        await Future.delayed(const Duration(milliseconds: 400));
        emit(UserLoaded('Alice (id=${e.id})'));
      } catch (err) {
        emit(UserError(err.toString()));
      }
    });
  }
}

// ---------- UI ----------
void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: MultiBlocProvider(
          providers: [
            BlocProvider(create: (_) => CounterBloc()),
            BlocProvider(create: (_) => UserBloc()),
          ],
          child: const HomePage(),
        ),
      );
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Bloc demo')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            BlocBuilder<CounterBloc, int>(
              builder: (_, n) => Text('Count: $n', style: const TextStyle(fontSize: 24)),
            ),
            const SizedBox(height: 8),
            FilledButton(
              onPressed: () => context.read<CounterBloc>().add(Increment()),
              child: const Text('Increment'),
            ),
            const SizedBox(height: 24),
            BlocBuilder<UserBloc, UserState>(
              builder: (_, s) => switch (s) {
                UserInitial() => const Text('No user loaded'),
                UserLoading() => const CircularProgressIndicator(),
                UserLoaded(:final name) => Text('Loaded: $name'),
                UserError(:final message) => Text('Error: $message'),
              },
            ),
            FilledButton(
              onPressed: () => context.read<UserBloc>().add(LoadUser('42')),
              child: const Text('Load user'),
            ),
          ],
        ),
      ),
    );
  }
}

Why it matters

Sealed event/state classes plus the Dart 3 switch expression turn Bloc into something close to a state-machine you can read top to bottom. If a state needs more than three or four fields, split the screen into smaller Blocs rather than letting one accumulate the whole feature.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
class CounterCubit extends Cubit<int> {
    CounterCubit() : super(0);
    void inc() => emit(state + 1);
}

BlocProvider(
    create: (_) => CounterCubit(),
    child: BlocBuilder<CounterCubit, int>(
        builder: (c, n) => Text('$n'),
    ),
);
Try it Yourself »

Discussion

Loading…