BLoC: Widget Integration, Testing & Best Practices
Providing & Consuming a BLoC
// Provides a LoginBloc instance to the widget subtree below --
// no manual threading through every constructor needed
BlocProvider(
create: (context) => LoginBloc(authService),
child: LoginScreen(),
)
// BlocBuilder rebuilds when a new state is emitted
BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
if (state is LoginLoading) return CircularProgressIndicator();
if (state is LoginSuccess) return Text('Welcome, ${state.user.name}');
return LoginForm();
},
)
// BlocListener runs a side effect (snackbar, navigation) WITHOUT
// rebuilding any UI itself
BlocListener<LoginBloc, LoginState>(
listener: (context, state) {
if (state is LoginFailure) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.message)));
}
},
child: LoginForm(),
)Dispatching Events: read() vs. watch()
// context.read<T>() -- gets the bloc WITHOUT subscribing to changes,
// appropriate inside a one-time callback like onPressed
ElevatedButton(
onPressed: () => context.read<LoginBloc>().add(LoginSubmitted(email, password)),
child: Text('Log in'),
)
// context.watch<T>() -- subscribes to state changes, triggering a
// rebuild whenever a new state is emitted (used inside build())Testing with bloc_test
// Tests the exact state sequence emitted -- no widget rendering needed
blocTest<LoginBloc, LoginState>(
'emits [LoginLoading, LoginSuccess] on successful login',
build: () => LoginBloc(authService),
act: (bloc) => bloc.add(LoginSubmitted('a@b.com', 'pass')),
expect: () => [LoginLoading(), LoginSuccess(testUser)],
);Value Equality with Equatable
// BlocBuilder compares previous/new state to decide if a rebuild
// is needed -- without value equality, identical-content states with
// different object instances would cause unnecessary rebuilds
class LoginSuccess extends LoginState with EquatableMixin {
final User user;
LoginSuccess(this.user);
@override
List<Object> get props => [user];
}Choosing setState vs. BLoC
A simple single-screen widget might reasonably use setState() with no real downside. BLoC's structured separation pays off as an app grows: sharing state across distant screens, and testing business logic independently of any widget rendering.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free