Problem Statement
Explain the differences between BlocBuilder, BlocListener, and BlocConsumer. When should you use each?
Explanation
BlocBuilder rebuilds its child widget tree when state changes, used for updating UI declaratively based on current state. It's the primary way to render UI that reacts to state. Use BlocBuilder when state changes should result in different widget trees, like showing different screens, updating text, or changing layouts. The buildWhen parameter optimizes rebuilds by specifying which state changes trigger rebuilds.
BlocListener executes code in response to state changes without rebuilding UI, perfect for side effects like navigation, dialogs, snackbars, or analytics. It doesn't return a widget from its listener function - just performs actions. Use BlocListener when state changes require actions beyond UI updates, like navigating after successful login or showing error dialogs. The listenWhen parameter filters which state changes trigger the listener.
BlocConsumer combines both - it rebuilds UI and performs side effects, useful when you need to do both in response to state changes. For example, showing a success screen (rebuild) and displaying a toast (side effect) when an operation completes. BlocConsumer has both builder and listener parameters plus buildWhen and listenWhen for fine control.
Use BlocBuilder for most cases where you just need UI updates. Add BlocListener when you need side effects in addition to UI, placing it strategically where side effects make sense (near Navigator for navigation, near Scaffold for SnackBars). Use BlocConsumer when you'd otherwise nest BlocBuilder inside BlocListener, providing cleaner code. Understanding when to use each prevents common mistakes like navigation in BlocBuilder or excessive rebuilds from BlocListener.

