CodeBlanks Logo Icon CodeBlanks

BlocListener

Handle side effects with BlocListener.

What is BlocListener?

BlocListener is a widget that runs a side effect when a Bloc or Cubit state changes. Tasks like showing a snackbar, navigating, or opening a dialog are done with BlocListener instead of BlocBuilder. The listener callback is called once per state change.

BlocListener<LoginBloc, LoginState>(
  listener: (context, state) {
    if (state is LoginSuccess) {
      Navigator.pushNamed(context, '/home');
    }
  },
  child: Text('Login'),
)

listenWhen

The listenWhen callback conditions when the listener runs. For example, to run only on success you write listenWhen: (previous, current) => current is LoginSuccess. This prevents unnecessary side effects.

BlocListener<LoginBloc, LoginState>(
  listenWhen: (prev, curr) => curr is LoginSuccess,
  listener: (context, state) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Login success')));
  },
  child: Container(),
)

Practice Listen to the state stream

bloc.stream.listen is the foundation of BlocListener. Create a LoginBloc, declare a String? message. Listen to the stream and when the state is LoginSuccess, assign 'Welcome ${state.user}' to message. Trigger the event, wait, and print message.

Starter code

main.dart
import 'package:bloc/bloc.dart';

abstract class LoginEvent {}
class LoginSubmittedEvent extends LoginEvent {}

abstract class LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginLoading()) {
    on<LoginSubmittedEvent>((event, emit) =>
      emit(LoginSuccess('Ayşe')));
  }
}

void main() async {
  final bloc = LoginBloc();
  String? message;
  // stream dinleyici ekle
  // LoginSuccess olduğunda message'a 'Welcome ${state.user}' ata
  bloc.add(LoginSubmittedEvent());
  await Future.delayed(Duration(milliseconds: 10));
  // message'i yazdır
}

Sample solution

main.dart
import 'package:bloc/bloc.dart';

abstract class LoginEvent {}
class LoginSubmittedEvent extends LoginEvent {}

abstract class LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginLoading()) {
    on<LoginSubmittedEvent>((event, emit) =>
      emit(LoginSuccess('Ayşe')));
  }
}

void main() async {
  final bloc = LoginBloc();
  String? message;
  bloc.stream.listen((state) {
    if (state is LoginSuccess) {
      message = 'Welcome ${state.user}';
    }
  });
  bloc.add(LoginSubmittedEvent());
  await Future.delayed(Duration(milliseconds: 10));
  print(message);
}

Expected output

Welcome Ayşe

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 When is BlocListener used instead of BlocBuilder?

  • To fetch data from the Bloc
  • When the UI needs to be rebuilt
  • Just to read the state
  • When a side effect (navigation, snackbar) is needed Correct answer

Explanation: BlocBuilder rebuilds the UI; BlocListener performs side effects and does not produce a widget.

2 BlocListener parameter

Question
BlocListener<LoginBloc, LoginState>(
  ____: (context, state) {
    if (state is LoginSuccess) {
      print('Giriş başarılı');
    }
  },
  child: Container(),
)
Answer
BlocListener<LoginBloc, LoginState>(
  listener: (context, state) {
    if (state is LoginSuccess) {
      print('Giriş başarılı');
    }
  },
  child: Container(),
)

Explanation: The side-effect callback of BlocListener is the listener parameter; builder is used in BlocBuilder.

3 BlocListener parts

Term Meaning
BlocListener Side effect widget
listener Runs on state change
listenWhen Condition to run
child Child widget

All lessons in Advanced