BlocListener
- Flutter course
- Advanced
- Lesson 9/69
- 4 min
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
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
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
BlocListener<LoginBloc, LoginState>(
____: (context, state) {
if (state is LoginSuccess) {
print('Giriş başarılı');
}
},
child: Container(),
)
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 |